一.继承相关概念
二.派生类的定义
代码举例如下:
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;//no
b = 10;//yes
c = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用
}
protected:
};
int main()
{
Child c1;
c1.a = 10;//no
c1.b = 10;//yes
c1.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;//no
b = 10;//yes
c = 10;//yes,父类中是protected所以只能在父类内部和子类内部使用
}
protected:
};
int main()
{
Child c1;
c1.a = 10;//no
c1.b = 10;//no
c1.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;//no
b = 10;//yes
c = 10;//yes
}
protected:
};
int main()
{
Child c1;
c1.a = 10;//no
c1.b = 10;//no
c1.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;//加上virtual
cout << sizeof(base2) << endl;//没有加virtual
system("pause");
return 0;
}
由运行结果可以看出,加上virtual的编译器给变量偷偷的增加了属性,这就是虚继承的一种手段~
具体为什么会多了四个字节,增加的 属性是什么呢?接下来我们具体分析
我就直接开门见山的说了,解释由下面图示,首先从内存看起
这就是虚继承的本质!