C++继承

来源:互联网 发布:二手手机市场软件 编辑:程序博客网 时间:2024/06/06 07:36

一.继承相关概念


二.派生类的定义


代码举例如下:

class Parent//基类{private:int a;public:void print(){cout << "a=" << a << endl;}protected:};class Child :public Parent//派生类,这里的public 可以也可以是private或者protected{private:int b;public:protected:};
三.继承的重要说明:

1.子类拥有父类的所有成员变量和成员函数

2.子类就是一种特殊的父类

3.子类对象可以当作父类对象使用

4.子类可以拥有父类没有的方法和属性

四.派生类的访问控制:

1.如若是public继承,父类在子类中保持原有访问类别

2.如若是protect继承,父类的public变为protected,private依然是private,protected依然是protected

3.如若是private继承,父类在子类中都是private

对于1的代码测试如下:

①public继承

class Parent//基类{private:int a;public:int b;protected:int c;};class Child :public Parent//public继承{private:public:void fun(){a = 10;//nob = 10;//yesc = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用}protected:};int main(){Child c1;c1.a = 10;//noc1.b = 10;//yesc1.c = 10;//no}
②private继承
class Parent//基类{private:int a;public:int b;protected:int c;};class Child :private Parent//private继承过来以后在子类中改变了访问属性{private:public:void fun(){a = 10;//nob = 10;//yesc = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用}protected:};int main(){Child c1;c1.a = 10;//noc1.b = 10;//noc1.c = 10;//no}
③protected继承
class Parent//基类{private:int a;public:int b;protected:int c;};class Child :protected Parent//private继承过来以后在子类中改变了访问属性{private:public:void fun(){a = 10;//nob = 10;//yesc = 10;//yes}protected:};int main(){Child c1;c1.a = 10;//noc1.b = 10;//noc1.c = 10;//no}
五.继承中的构造和析构

1.类型兼容性原则:在需要基类对象的任何地方都可以使用公有子类对象代替,通过公有继承派生类得到了基类出构造和析构外的所有成员

①子类对象可以当作父类对象使用

②子类对象可以直接赋值给父类对象

③子类对象可以直接初始化父类对象

④父类指针可以直接指向子类对象

⑤父类引用可以引用子类对象

class Parent//基类{private:int a;public:void print(){cout << "我是父类" << endl;    }protected:};class Child :public Parent{private:int c;public:protected:};int main(){Child c1;Parent *p = NULL;p = &c1;//父类指针指向子类对象p->print();//调用父类函数Parent &n = c1;//子类引用初始化父类n.print();//打印父类函数system("pause");return 0;}
六.继承中的对象模型和继承中的构造和析构

问题的引出,首先看下面的代码:


答:①在子类对象构造时,需要调用父类对象的构造函数对其继承来的成员进行初始化

        ②在子类对象析构时,需要调用父类析构函数对其继承得到对象成员进行清理

继承中构造和析构的调用原则:

①子类对象在创建时首先调用父类的构造函数

②父类构造函数执行结束后,执行子类构造函数

③当父类构造函数中有参数时,需要在子类的初始化列表中显示调用

④析构函数调用的顺序与构造函数相反

代码举例如下:

class parent{public:parent(int x, int y){this->a = x;this->b = y;cout << "父类构造函数" << endl;}~parent(){cout << "父类析构函数" << endl;system("pause");}private:int a;int b;};class child:public parent{public:child(int x, int y, int z) :parent(x,y)//因为父类构造函数中有参数,所以需要用参数初始化列表显示调用{this->c = z;cout << "子类构造函数" << endl;}~child(){cout << "子类析构函数" << endl;}private:int c;};void test()//为了更好的显示对象的周期{child c1(1, 2, 3);}int main(){test();system("pasue");return 0;}
刚好符合上面的规则

七.继承与组合混搭情况下的构造和析构的调用原则

原则:

先构造父类,在构造成员变量,最后构造自己

先析构自己,再析构成员变来那个,最后析构父类

代码如下:

class grandfather{private:int ch;int xl;public:grandfather(int w,int m){this->ch = w;this->xl = m;cout << "ch=" << ch << "  xl=" << xl << endl;cout << "执行爷爷的构造函数"<<endl;}~grandfather(){cout << "执行爷爷的析构函数" << endl;}};class parent :public grandfather{public:parent(int w,int m,int x, int y) :grandfather(w,m){this->a = x;this->b = y;cout << "a=" << a << "  b=" << b << endl;cout << "父类构造函数" << endl;}~parent(){cout << "父类析构函数" << endl;}private:int a;int b;};class child:public parent{public:child(int x, int y, int m, int w, int z) :parent(x, y, m,w), obj1(x,y),obj2(3,4)//因为父类构造函数中有参数,所以需要用参数初始化列表显示调用{this->c = z;cout << "c=" << z << endl;cout << "子类构造函数" << endl;}~child(){cout << "子类析构函数" << endl;}private:int c;grandfather obj1;grandfather obj2;};void test()//为了更好的显示对象的周期{child c1(1,2,3,4,5);}int main(){test();system("pause");return 0;}

八.继承中同名成员变量和函数的处理方法

方法:通过作用域区分符去区分

1.重名成员


测试代码如下:

class parent{public:int a;int b;void print(){cout << "a=" << a << endl;}};class child :public parent{public:int a;int b;void get(){cout << "a=" << a << endl;}};int main(){child c2;c2.a = 20;c2.get();//调用子类函数c2.print();//调用父类函数system("pause");return 0;}

2.重名函数


测试代码:

class parent{public:void print(){cout <<"父类打印函数"<< endl;}};class child :public parent{public:void print(){cout <<"子类打印函数" << endl;}};int main(){child c2;c2.print();system("pause");return 0;}
所以默认是子类的函数。

十.派生类的static关键字

下面将用代码实例来说明:

class parent{public:parent(){cout << "父类构造函数" << endl;}void print(){cout <<"父类打印函数"<< endl;}public:static int a;};int parent::a = 100;//告诉编译器给static成员分配空间,告诉编译我在继承中用到a,不然会报错class child :public parent//私有继承{public:void print1(){cout << a << endl;cout <<"子类打印函数" << endl;}};int main(){   //①static也遵循派生类访问控制,私有继承,所以外界不可访问//child c1;//c1.a = 200;//不可访问//② int parent::a = 100;//告诉编译器给static成员分配空间,告诉编译我在继承中用到a,不然会报错  //现在将int parent::a=100屏蔽开始测试//child c1;//c1.print();//可以通过//c1.print1();//无法通过,因为没有给a分配内存空间加上int parent::a=100就可以通过system("pause");return 0;}
十一.多继承

1.

代码实例:

class base1{private:int a;public:base1(int a){this->a = a;}void print1(){cout << this->a << endl;}};class base2{private:int b;public:base2(int b){this->b= b;}void print2(){cout << this->b<< endl;}};class stu:public base1,public base2{private:int c;public:stu(int a, int b, int c) :base1(a), base2(b){this->c = c;}void print3(){cout << this->c << endl;}};int main(){stu c1(1, 2, 3);c1.print1();c1.print2();c1.print3();system("pause");return 0;}
2.多继承二义性(虚继承)
对于二义性的解释:


代码如下:

class A{public:int z;};class base1:public A{public:int a;};class base2:public A{public:int b;};class stu:public base1,public base2{public:int c;};int main(){stu c1;c1.z;//访问z就会出问题system("pause");return 0;}

怎么解决上述问题呢?这就需要用到虚继承,给的公有继承public前面加上关键字virtual就解决了

class base1:virtual public A{public:int a;};class base2:virtual public A{public:int b;};

编译就可以通过了。

不过虚继承只能解决这种有公有继承祖先的多继承,菱形继承,比如下面这个例子加上virtual就无法解决

class B1{public:int a;};class B2{public:int a;};class C :virtual public B1, virtual public B2{public:};int main(){C c1;c1.a;//编译出错,访问不明确,如下图system("pause");return 0;}

这种问题只能通过我们自己解决,加上域限制符

class B1{public:int a;};class B2{public:int a;};class C : public B1,public B2{public:};int main(){C c1;c1.B1::a=100;c1.B2::a=200;system("pause");return 0;}

这样就可以了。

补充,在菱形继承中加上virtual和不加vitual的父类大小有区别吗?接下来我们做个实验

代码如下:

class A{public:int z;};class base1:virtual public A//加上virtual{public:int a;};class base2:public A//没有加virtual{public:int b;};class stu:public base1,public base2{public:int c;};int main(){cout << sizeof(A) << endl;cout << sizeof(base1) << endl;//加上virtualcout << sizeof(base2) << endl;//没有加virtualsystem("pause");return 0;}

由运行结果可以看出,加上virtual的编译器给变量偷偷的增加了属性,这就是虚继承的一种手段~

具体为什么会多了四个字节,增加的 属性是什么呢?接下来我们具体分析

我就直接开门见山的说了,解释由下面图示,首先从内存看起



这就是虚继承的本质!

原创粉丝点击