编程语言
134
一、继承方式和访问方式
注意:友元不能继承
,静态成员只会在父类创造一次
父类如果用private修饰的成员,子类无法访问 protected修饰的子类可以访问,但除此之外不可访问
继承方式为private,struct为public ,一般继承方式都为public 基类的成员在子类的访问方式 = ==Min(成员在基类的访问限定符,继承方式)
二、赋值兼容规则
int main() { //1.子类对象可以赋值给父类对象/指针/引用 person p; student s; p = s;//对象赋值 s = p;//不行 //子类赋值给父类,通过切割(切片)可以进行赋值 person* ptr = &s; //指针 person& ref = s; //引用 //student* sptr = &p; //父类赋值给子类,不行 student* sptr = (student*)&s; //ptr指向的本来就是子类,强制转换下可以赋值 sptr = (student*)&p; //强行转换可以运行,但容易导致越界访问,访问到子类独有的类 //也可以通过C++11中的类型操作符来进行试探 sptr = dynamic_cast<student*>(ptr); //注意此时父类必须有虚函数构成多态 //如果ptr指向父类,返回nullptr //student& sref = p;//引用赋值,不行 return 0; }
三、默认函数的继承
class person { public: person(const char* name = "peter") :_name(name) { cout << "person()" << endl; } person(const person& p) :_name(p._name) { cout << "person(const person& p)" << endl; } person& operator=(const person& p) { cout << "person& operator=(const person& p)" << endl; if (this != &p) { _name = p._name; } return *this; } ~person() { cout << "~person()" << endl; } protected: string _name; }; class student : public person { public: student(const char* name, int num) ://_name(name) 父类归父类管,所以这样初始化错误 person(name) //直接调用父类的构造就行了 , _num(num) { cout << "student()" << endl; } student(const student& s) ://_name(s._name) person(s) //同上 ,_num(s._num) { cout << "student(const student& s)" << endl; } student& operator=(const student& s) { cout << "student& operator=(const student& s)" << endl; if (this != &s) { //operator=(s); 此时会无限递归,自己调用自己,要声明域才行 person::operator=(s); _num = s._num; } return *this; } ~student() //子类的析构函数和父类的析构函数构成 隐藏(重定义) ,他们的名字会被编译器同一处理成destructor { //person::~person(); //派生类没必要再调用父类的析构函数,析构的顺序不对,不符合栈形式 //结束时会自动调用父类的析构函数,因为这样才能保证先析构子类,再析构父类 //并且如果再调用会导致重复释放同一块空间 cout << "~student()" << endl; } private: int _num; }; int main() { student s1("jack", 18); student s2(s1); student s3("rose", 19); s1 = s3; return 0; }
四、不能被继承的类
1.将父类全部私有,使得子类无法调用父类的析构 2.利用final关键字
class A final{public:A(){}}; class B :public A{}; //此时B就无法继承A
五、菱形继承
1.菱形继承的问题
//单继承:一个子类只有一个直接定义父类时称为单继承 class person{}; class student : public person{}; class teacher : public student{}; //多继承:一个子类有两个以上或以上直接父类时称为多继承 class student{}; class teacher{}; class assistant : public student,public teacher{}; //菱形继承 class person {}; class student : public person {}; class teacher : public student {}; class assistant : public student, public teacher {}; //菱形继承的问题是:数据冗余和二义性
菱形继承时有两个问题 数据冗余:即派生类有多份相同类型的数据 二义性: 即因为有多相同类型的数据而导致对数据处理时,不知道要处理谁
class person { public: string _name; }; class student :virtual public person //中间人都加上virtual 即可解决 { protected: int _num; }; class teacher :virtual public person //中间人都加上virtual 即可解决 { protected: int _id; }; class assistant : public student, public teacher { protected: string _course; }; void main() { assistant a; a._name = "peter"; //此时二义性导致不知道要赋给哪个_name,加上关键词virtual即可解决二义性和冗余性 //显示指定访问可以解决,但是冗余性还是没有解决(两个name) a.student::_name = "xxx"; a.teacher::_name = "yyy"; }
2.菱形继承virtual解决的原理
class A { public: int _a; //int _a[10000]; }; class B :virtual public A { public: int _b; }; class C :virtual public A { public: int _c; }; class D : public B, public C { public: int _d; }; int main() { D d; cout << sizeof(d) << endl; d.B::_a = 1; d.C::_a = 2; d._b = 3; d._c = 4; d._d = 5; d._a = 6; return 0; } //所以实际上,不到万不得已,不要把类的关系设计为菱形继承 //virtual解决了冗余性和二义性,但是会有略微的效率损失 //这个例子size变大了,但是如果父类成员size很大,那么在virtual继承后会减小很多
六、继承和组合
class A{}; class B : public A{}; //继承是白箱复用,父类对子类的是透明的,但是一定程度上破坏了父类的封装,继承的类是一种高耦合 class C{}; class D //组合是黑箱封装,C对D不透明,C保持他的封装,组合的类耦合度更低 {C c;}; //都完成了类层次的复用 //综合来看,还是组合合适 //符合 is-a 使用继承 ,eg:奔驰是车 //符合 has-a 使用组合 ,eg:车有轮子 //如果都可以,优先使用组合