类的继承:
通过继承来减少代码量与提高复用性,一个基本例子:
class Father {
public:
int age;
string name;
Father(): age(30),name("Father") {}
}
class Son: public Father {
public:
int examScore;
Son(): examScore(100) { // 不可以在这里初始化age和name
// 值得注意的是,继承来的成员属性只能在方法内更改,不可以使用初始化列表更改。
age = 10;
name = "Son";
}
}
访问权限:
可以用来控制成员变量与函数能否被访问,有private、protected和public三种。其访问权限如下:
继承类型:
我们一般就用public来继承,但不排除特殊情况,也是有private、protected和public三种,其继承含义如下:
① 公有继承(public):父类的public和protected成员都成为了子类的public和protected成员,但父类的private成员不能直接访问,可以通过public或protected的方法来访问或修改。
② 保护继承(protected):父类的public和protected成员都变成了子类的protected成员。
③ 私有继承(private):父类的public和protected成员都成为了子类的private成员。
class Father {
public:
int a;
protected:
int b;
private:
int c;
};
class Son1 : public Father { // public继承
public:
void show() {
cout << a << endl; // 可以输出
cout << b << endl; // 可以输出
cout << c << endl; // 报错,因为父类的c是private的
}
};
class Son2 : protected Father { // protected继承
public:
void show() {
cout << a << endl; // 可以输出
cout << b << endl; // 可以输出
cout << c << endl; // 报错,因为父类的c是private的
}
};
class Son3 : private Father { // private继承
public:
void show() {
cout << a << endl; // 可以输出
cout << b << endl; // 可以输出
cout << c << endl; // 报错,因为父类的c是private的
}
};
class GrandSon : public Son3 { //public继承,但父类是Son3
public:
void show() {
// 三个输出都报错,因为Son3里继承的abc都变成了private的了
cout << a << endl;
cout << b << endl;
cout << c << endl;
}
};
int main() {
// Father f;
// cout << f.a << endl; // 可以输出
// cout << f.b << endl; // 报错,protected的
// cout << f.c << endl; // 报错,private的
// Son1 s1;
// cout << s1.a << endl; // 可以输出
// cout << s1.b << endl; // 报错,protected的
// cout << s1.c << endl; // 报错,private的
// s1.show();
// Son2 s2;
// cout << s2.a << endl; // 报错,protected的
// cout << s2.b << endl; // 报错,protected的
// cout << s2.c << endl; // 报错,没有继承这个成员
// s2.show();
//Son3 s3;
//cout << s3.a << endl; // 报错,private的
//cout << s3.b << endl; // 报错,private的
//cout << s3.c << endl; // 报错,没有继承这个成员
// s3.show();
return 0;
}
子类的初始化:
当一个子类被初始化的时候,会先初始化它的父类,然后再初始化子类。
当一个子类被销毁的时候,会先销毁子类,再销毁它的父类。
class Father {
public:
Father() {
cout << "Father()" << endl;
}
~Father() {
cout << "~Father()" << endl;
}
};
class Son : public Father {
public:
Son() {
cout << "Son()" << endl;
}
~Son() {
cout << "~Son()" << endl;
}
};
int main {
Son s;
return 0;
}
上述程序的输出为:
Father()
Son()
~Son()
~Father()
有一种类似的情况,就是当一个类的成员变量是另一个类的实例对象时,也是先初始化这个成员变量所在的类,再初始化这个类本身:
class Father {
public:
Father() {
cout << "Father()" << endl;
}
~Father() {
cout << "~Father()" << endl;
}
};
class Son{ // 注意看,这个类没有继承
public:
Father s; // 但是他有一个Father的对象作为成员
Son() {
cout << "Son()" << endl;
}
~Son() {
cout << "~Son()" << endl;
}
};
int main() {
Son s;
return 0;
}
上述程序输出为:
Father()
Son()
~Son()
~Father()
子类与父类的冲突:
如果子类和父类定义了一个同名的成员,那么会优先调用子类的:
class Father {
public:
char dream;
Father() : dream('b') {}
};
class Son : public Father {
public:
char dream;
Son() : dream('c') {}
};
int main() {
Son s;
cout << s.dream << endl; //输出的是c而不是b。
cout << s.Father::dream << endl; //这样就可以访问父类的dream了,输出的是b
}
同理,如果子类和父类有一个同名的函数,会优先执行子类的。但值得注意的是,如果父类里面存在函数的重载,那么如果子类重写了父类的某一个函数,那么就没有办法继承来自父类的原来的重载的函数,如果子类没有重写任何一个父类的包含重载的函数,那么子类将能继承父类所有的重载函数。(别看了,太tm绕了。。。
多继承:
一个子类可以有多个父类(但人不行啊),多继承不太常用,仅举个例子:
class Father {
public:
int height;
int age;
};
class Mother {
public:
int beauty;
int age;
};
class Son : public Father, public Mother {
};
void main() {
Son s;
cout << s.age << endl; // 报错,因为同时继承了两个age,下面的菱形继承会解决这种情况
cout << s.Father::age << endl; // 正常输出
cout << s.Mother::age << endl; // 正常输出
cout << s.height << endl;// 正常输出
cout << s.beauty << endl;// 正常输出
}
菱形(钻石)继承:
菱形继承也是子类和父类的一种冲突,比如如下情况:
class Animal {
public:
int age;
};
class Yang: public Animal {};
class Tuo: public Animal {};
class YangTuo: public Yang, public Tuo {};
可以看到,羊驼继承了羊和驼,但是,羊和驼都继承了动物,也就是说,羊驼有两个age了,这是不应该的,产生了冲突。因此,我们需要使用虚拟继承来解决这个问题:
class Animal {
public:
int age;
};
class Yang: virtual public Animal {}; // 虚拟继承
class Tuo: virtual public Animal {}; // 虚拟继承
class YangTuo: public Yang, public Tuo {};
int main() {
YangTuo s;
s.age = 18;
s.Yang::age = 28;
s.Tuo::age = 38;
cout << s.age << endl; // 输出38
cout << s.Yang::age << endl; // 输出38
cout << s.Tuo::age << endl; // 输出38
return 0;
}
通过虚拟继承,可以解决上述问题,但奇怪的是,上面的程序的三个输出都是38,也就是,这三个age指向的是同一个变量!C++为了解决菱形继承的问题,特别引入了这么一种特殊的机制,如果存在菱形继承的错误,那么就会使用一个类似指针的东西,去保存这个冲突的变量,然后让冲突的子类和父类都共享这个指针。
子类继承父类后占用空间:
按照直觉去想,因为子类没办法继承父类的private成员,那么应该不会因为父类的private成员而消耗内存,实际上不是这样的:
class Father {
public:
int id;
protected:
int age;
private:
int bank_card;
};
class Son : public Father {
public:
int n_toy;
};
int main() {
Father f;
Son s;
cout << sizeof(f) << endl; // 输出12,三个int,每个int占4字节
cout << sizeof(s) << endl; // 输出16,说明父类的private成员也被继承下来了!
return 0;
}
原因其实很简单,因为其实子类是可以通过调用父类的public或protected方法来访问父类的private变量的,所以也要把父类的private继承下来,但只是不能直接访问罢了。
当然,其实没那么简单,上述例子只讨论了全部成员都是同一个类型的情况,如果类型不同,那么就涉及到内存对齐的问题了,但这个问题非常复杂,这里不做讨论。