1. 语法
-
原则:is-a (什么是什么)
父类/子类
基类/派生类 -
语法
class 派生类 : [访问限定符] 基类 {
成员
}
如果不写继承访问限定符,默认是private
a.继承的时候一定是public
class EqualTriangle:public Triangle{ //a.继承的时候一定是public
b.构造函数的写法:父类构造i函数初始化列表;
EqualTriangle(int bottom, int side):Triangle(bottom,side,side){} //b.构造函数的写法:父类构造i函数初始化列表;
- 完整案例
#include <iostream>
using namespace std;
#include <cmath>
class Triangle{
int a,b,c;
public:
Triangle(int a, int b, int c):a(a),b(b),c(c){}
int GetLength()const{
return a + b + c;
}
int GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
};
class EqualTriangle:public Triangle{ //a.继承的时候一定是public
public:
EqualTriangle(int bottom, int side):Triangle(bottom,side,side){} //b.构造函数的写法:父类构造i函数初始化列表;
};
int main (){
cout << sizeof(EqualTriangle) <<endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << endl;
}
c.子类不能访问父类的私有变量,子类想直接访问父的变量,但是为私有的情况;
int GetBottom()const{ //子类想直接访问父的变量,但是为私有的情况;
return a;
}
解决方法:
- protected:不允许外部对象访问,但是子类是可以的;
protected: //解决之道1:protected:不允许外部对象访问,但是子类是可以的;
- 成员函数的方案
int GetA()const{return a;} //解决之道2:成员函数的方案
- 完整案例
#include <iostream>
using namespace std;
#include <cmath>
class Triangle{
//private: //a.子类不能访问父类的私有变量
protected: //解决方法1:protected:不允许外部对象访问,但是子类是可以的;
int a,b,c;
public:
Triangle(int a, int b, int c):a(a),b(b),c(c){}
int GetLength()const{
return a + b + c;
}
int GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
//int GetA()const{return a;} //解决方法2:成员函数的方案
};
class EqualTriangle:public Triangle{
public:
EqualTriangle(int bottom, int side):Triangle(bottom,side,side){}
int GetBottom()const{ //子类想直接访问父的变量,但是为私有的情况;
return a;
}
int GetSide()const{
return b;
}
};
int main (){
cout << sizeof(EqualTriangle) <<endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << end;
}
2. 成员的访问权限
- | public | protected | private |
---|---|---|---|
类成员函数 | √ | √ | √ |
友元函数 | √ | √ | √ |
子类函数 | √ | √ | × |
类对象 | √ | × | × |
子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
子类访问父类成员变量,把父类成员变量访问限制符,改为protected。
- 案例
a. 在类里面,可以访问到内部定义的任何成员
b. 对象只能访问类定义的public成员
c. __ func__ 函数返回函数名,左右为两个下划线
#include <iostream>
using namespace std;
class Simple{
public:
void Test(){ // 在类里面,可以访问到内部定义的任何成员。
cout << __func__ << endl;
Public();
Protected();
Private();
}
void Public(){
cout << __func__ << endl;
}
protected:
void Protected(){
cout << __func__ << endl;
}
private:
void Private(){
cout << __func__ << endl;
}
};
int main(){
Simple t;
// t.Test(); // 对象调用的类型里面的公有的成员函数,可以访问内部的所有成员(变量+函数)
t.Public(); // 对象只能访问类定义的public成员
// t.Protected();
// t.Private();
}
子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
子类访问父类成员变量,把父类成员变量访问限制符,改为protected。
- 继承访问权限变化
分为子类内部和子类对象两种访问方式。
- 子类内部访问父类成员
- | public | protected | private |
---|---|---|---|
public 继承 | √ | √ | × |
protected 继承 | √ | √ | × |
private 继承 | √ | √ | × |
子类内部访问父类成员,只能访问public和protected成员。
- 子类对象访问父类成员
- | public | protected | private |
---|---|---|---|
public 继承 | √ | × | × |
protected 继承 | × | × | × |
private 继承 | × | × | × |
子类只有public继承父类的时候,才能访问父类的public成员,其他都不能访问。
通常子类使用public继承父类。
- 子类对象访问父类成员访问限定符的变化
继承方式\父类成员 | public | protected | private |
---|---|---|---|
public 继承 | public | protected | 不可见 |
protected 继承 | protected | protected | 不可见 |
private 继承 | private | private | 不可见 |
(public 继承)
a. public继承,父类public成员以public继承到子类,protected成员以protecte 继承到子类
public、protected、private继承时,子类里面可以访问除了private所有成员
class Drivce:public Simple{
public:
void DrivceTest(){
// public继承,父类public成员以public继承到子类,protected成员以protecte d继承到子类
//public、protected、private继承时,子类里面可以访问除了private所有成员
Public();
Protected();
// Private();
}
};
b.public继承时,子类对象只可以访问父类的public成员
d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
// d.Private();
- 完整代码
#include <iostream>
using namespace std;
class Simple{
public:
void Test(){ // 在类里面,可以访问到内部定义的任何成员。
cout << __func__ << endl;
Public();
Protected();
Private();
}
void Public(){
cout << __func__ << endl;
}
protected:
void Protected(){
cout << __func__ << endl;
}
private:
void Private(){
cout << __func__ << endl;
}
};
class Drivce:public Simple{
public:
void DrivceTest(){
// public继承,父类public成员以public继承到子类,protected成员以protected 继承到子类
//public protected、private继承时,子类里面可以访问除了private所有成员
Public();
Protected();
// Private();
}
};
class SubDivce:public Drivce{
public:
void SubDivceTest(){
Public();
Protected();
}
};
int main(){
Simple t;
Drivce d;
d.DrivceTest();
d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
// d.Private();
}
(protected 继承)
a. protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类
class Drivce:protected Simple{
public:
void DrivceTest(){
// public、protected、private继承时,子类里面可以访问除了private所有成员
// protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类
Public();
Protected();
// Private();
}
};
b.protected、private继承时,子类对象不能访问父类的所有成员
d.DrivceTest();
// d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
// d.Private();
- 完整案例
#include <iostream>
using namespace std;
class Simple{
public:
void Test(){ // 在类里面,可以访问到内部定义的任何成员。
cout << __func__ << endl;
Public();
Protected();
Private();
}
void Public(){
cout << __func__ << endl;
}
protected:
void Protected(){
cout << __func__ << endl;
}
private:
void Private(){
cout << __func__ << endl;
}
};
class Drivce:protected Simple{
public:
void DrivceTest(){
// public、protected、private继承时,子类里面可以访问除了private所有成员
// protected继承,父类public成员以protected继承到子类,protected成员以protected继承到子类
Public();
Protected();
// Private();
}
};
class SubDivce:public Drivce{
public:
void SubDivceTest(){
Public();
Protected();
}
};
int main(){
Simple t;
Drivce d;
d.DrivceTest();
// d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
// d.Private();
}
(private 继承)
a.public、protected、private继承时,子类里面可以访问除了private所有成员,
private继承 , 父类public成员以private 继承到子类,potected 成员以 private 继承到子类
class Drivce:private Simple{
public:
void DrivceTest(){
// public、protected、private继承时,子类里面可以访问除了private所有成员
// private继承 private private
Public();
Protected();
// Private();
}
};
b.protected、private继承时,子类对象不能访问父类的所有成员
d.DrivceTest();
// d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
- 完整案例
#include <iostream>
using namespace std;
class Simple{
public:
void Test(){ // 在类里面,可以访问到内部定义的任何成员。
cout << __func__ << endl;
Public();
Protected();
Private();
}
void Public(){
cout << __func__ << endl;
}
protected:
void Protected(){
cout << __func__ << endl;
}
private:
void Private(){
cout << __func__ << endl;
}
};
class Drivce:private Simple{
public:
void DrivceTest(){
// public、protected、private继承时,子类里面可以访问除了private所有成员
// private继承 private private
Public();
Protected();
// Private();
}
};
int main(){
Simple t;
Drivce d;
d.DrivceTest();
// d.Public(); // public继承时,子类对象只可以访问父类的public成员 ,protected、private继承时,子类对象不能访问父类的所有成员
// d.Protected();
}
小结
- public 无论类内部还是类对象都可以访问。
- protected 类对象不可访问,类内部与继承类的内部可以访问。
- private 只有类内部可以访问。
3. 继承关系的构造顺序
- 派生类的构造函数与析构函数的调用顺序
- 派生类的构造函数调用顺序
父对象构造、成员变量(子类依赖的类)析构、子对象构造的顺序 - 派生类的析构函数调用顺序
子对象析构、成员变量(子类依赖的类)析构、父对象析构的顺序
即构造顺序:(1)父类;(2)子类依赖的类;(3)子类;析构顺序刚好相反; - 注意:默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
Son():m(1),Father(1){cout << __func__ <<endl;} //a.默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
- 完整案例
#include <iostream>
using namespace std;
class Member{
public:
Member(int i){cout << __func__ <<endl;}
~Member(){cout << __func__ <<endl;}
};
class Father{
public:
Father(int i){cout << __func__ <<endl;}
~Father(){cout << __func__ <<endl;}
};
class Son:public Father{
Member m;
public:
Son():m(1),Father(1){cout << __func__ <<endl;} //a.默认构造函数可以不传递参数,父类和子类都可以;但是当父类的构造函数为带参函数时,子类需要在构造函数中初始化列表;
~Son(){cout << __func__ <<endl;}
};
int main(){
Son s; //b.构造顺序:(1)父类;(2)子类依赖的类;(3)子类;
//析构顺序刚好相反;
}
没有默认构造函数的基类在派生类的初始化,必须在初始化列表中初始化。
4. 同名隐藏规则
a. 概念:子类的成员函数与基类成员函数同名,子类的函数将会隐藏基类的所有同名函数。
同名隐藏:如果子类与父类同名,子类会隐藏父类的成员函数;一方面,子类会隐藏父类的值,另一方面会调用自己的函数,从而引起类型不匹配;
函数匹配的方法,避免深层查找:(1)先子类后父类,找到,匹配;(2)名和参数进行匹配;(3)参数不匹配,则不再寻找
解决方法1:可以完全解决同名隐藏,实际调用中加入类名(调用父类)
s.Father::Test(2); // 解决方法一:可以完全解决同名隐藏(调用父类)
解决方法2:子类里面采用命名空间,解决名称相同,但是参数列表不同的情况;main()函数直接用子类对象调用;
(子类)using Father::Test; //b.2子类里面采用命名空间,解决名称相同,但是参数列表不同的情况;main()函数直接用子类对象调用;
(main函数)s.Test(3);
- 完整案例
#include <iostream>
using namespace std;
class Father{
public:
void Test(int i){
cout << "Father::Test(" << i <<")" << endl;
}
void Test(){
cout << "Father::Test" << endl;
}
};
class Son:public Father{
public:
using Father::Test; //b.2采用命名空间,解决名称相同,但是参数列表不同的情况;
void Test(){
cout << "Son::Test" << endl;
}
};
int main(){
Father f;
f.Test(1);
Son s;
s.Test();
s.Father::Test(2); //b.1 解决之道1;可以完全解决同名隐藏(调用父类)
s.Test(3);
}
- b. 不建议使用同名隐藏规则
可以在表示普遍方法中的特例中使用,例如直角三角形求面积 - 完整代码
#include <iostream>
#include <cmath>
using namespace std;
class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
int a,b,c;
public:
Triangle(int a,int b,int c):a(a),b(b),c(c){}
int GetLength()const{
return a+b+c;
}
float GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
// int GetA()const{return a;}
};
// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
EqualTriangle(int bottom,int side):Triangle(bottom,side,side){}
int GetBottom()const{
return a;
}
int GetSide()const{
return b;
}
};
class RightTriangle:public Triangle{
public:
RightTriangle(int a,int b):Triangle(a,b,sqrt(a*a+b*b)){}
float GetArea()const{
cout << "RightTriangle::GetArea()" << endl;
return a*b/2.0;
}
};
int main(){
cout << sizeof(EqualTriangle) << endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << endl;
cout << e.GetBottom() << endl;
cout << e.GetSide() << endl;
Triangle t(3,4,5);
cout << t.GetArea() << endl;
RightTriangle r(3,4);
cout << r.GetArea() << endl;
}
5. 函数同名的情况总结
名称 | 英语 |
---|---|
重载 | overload |
重写(覆盖) | override |
隐藏 | hide |
6. 赋值兼容
- 赋值兼容规则(和多态相关)
概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。
father 可以是儿子,但是儿子不可以是father;
子类就相当于父类的对象;
三种情况
情况1. 派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
//1.Base类
Base& operator=(const Base&){cout<<__func__<<endl;} //a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
//(2.main)
b = d;
//d = b; a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
对象切割(Object Slicing):在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值。(子类的成员则不接受,父类)
- 总结
对象赋值兼容的时机(调用拷贝构造函数)
a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
b. 函数参数对象传值
//(main)
Base b2(d); //b1.函数参数对象传值
c. 函数返回对象
//类外
void Func(Base b){}
Base Func(){
Derive d;
return d; //b2.函数返回对象
}
d. 注意:(1)加入默认构造函数;
(2)拷贝构造函数初始化列表。
//base类
Base():n(0){}
Base(const Base& b):n(b.n){cout << __func__<<endl;}
- 完整案例
#include <iostream>
using namespace std;
class Base{
public:
int n;
Base():n(0){}
Base(const Base& b):n(b.n){cout << __func__<<endl;}
//Base& operator=(const Base&){cout<<__func__<<endl;} //a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
};
class Derive:public Base{
public:
int m;
void Test(){
cout << __func__ <<endl;
}
};
//b对象赋值兼容的时机(调用拷贝构造函数)
//b1.函数参数对象传值
//b2.函数返回对象
//b3.注意:(1)加入默认构造函数;
//(2)拷贝构造函数初始化列表。
void Func(Base b){}
Base Func(){
Derive d;
return d; //b2.函数返回对象
}
int main(){
Base b;
Derive d;
d.n=1;
d.m=10;
b = d;
//d = b; a.派生类的对象可以赋值给基类对象,调用赋值运算符重载。反之,不可。
cout << b.n << endl;
//cout << b.m << endl;
cout << sizeof(b) << endl;
cout << sizeof(d) << endl;
Base b2(d); //b1.函数参数对象传值
cout << b2.n <<endl;
Func(b);
Func(d);
Func();
}
问题:
子类特有的成员变量能否被父类访问? 否
此时父类对象(base)的大小与子类对象(derive)大小是否一致? 否
设计一段代码验证(见上)
情况2. 派生类的对象可以初始化基类的引用。
a.派生类的对象可以初始化基类的引用
Derive d;
Base& fb = d; //a.派生类的对象可以初始化基类的引用
b.二者的地址是相同的;
cout << &d <<endl; //b.二者的地址是相同的;
cout << &fb <<endl;
c.类型决定大小和能否访问成员
大小:
cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
cout << sizeof(fb) <<endl; //c2. base的大小
访问:
d.Test(); //c3.
//c4.fb.Test(); //类型决定大小和能否访问成员
#include <iostream>
using namespace std;
class Base{
public:
int n;
Base():n(0){}
Base(const Base& b):n(b.n){cout << __func__<<endl;}
//Base& operator=(const Base&){cout<<__func__<<endl;}
};
class Derive:public Base{
public:
int m;
void Test(){
cout << __func__ <<endl;
}
};
int main(){
Derive d;
Base& fb = d; //a.派生类的对象可以初始化基类的引用
cout << &d <<endl; //b.二者的地址是相同的;
cout << &fb <<endl;
cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
cout << sizeof(fb) <<endl; //c2. base的大小
d.Test(); //c3.父类虽然指向子类对象,但是不能访问子类成员;
//c4.fb.Test(); //类型决定大小和能否访问成员
}
情况3. 派生类对象的地址可以赋给指向基类的指针。
指向基类对象的指针变量也可以指向派生类对象。
父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;
正确写法:
Base* pb = &d;
Func(&d); //Func(pb);
错误写法:
pb->Test();
pb->m;
//父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;
- 完整案例
#include <iostream>
using namespace std;
class Base{
public:
int n;
Base():n(0){}
Base(const Base& b):n(b.n){cout << __func__<<endl;}
//Base& operator=(const Base&){cout<<__func__<<endl;}
};
class Derive:public Base{
public:
int m;
void Test(){
cout << __func__ <<endl;
}
};
void Func(Base& b){}
void Func(Based* b){}
int main(){
Derive d;
Base& fb = d; //a.派生类的对象可以初始化基类的引用
cout << &d <<endl; //b.二者的地址是相同的;
cout << &fb <<endl;
cout << sizeof(d) <<endl; //c1.类型大小不同,d的大小
cout << sizeof(fb) <<endl; //c2. base的大小
d.Test(); //c3.父类虽然指向子类对象,但是不能访问子类成员;
//c4.fb.Test(); //类型决定大小和能否访问成员
Func(d);
Base* pb = &d;
//pb->Test();
//pb->m;
//父类引用子类对象与父类指针指向子类对象,都只能访问子类继承的那部分成员,用法相同,写法不同;
Func(&d);
}
向上转换:派生类对象赋值给基类
向下转换:基类对象赋值给派生类
(2)其他其情况
- 派生类的对象可以赋值给基类的对象
int main() {
Base base;
Derive derive;
base = derive;
base.show(123);
//base.show();
//base.Derive::show();
}
- 派生类对象的地址赋给基类的指针变量
int main() {
Base* pbase = new Derive;
pbase->show(123);
//pbase->show();
//pbase->Derive::show();
}
指针访问派生类中由基类继承来的对象,不能访问派生类中的新成员
- 派生类对象可以初始化基类的引用
int main() {
Derive derive;
Base& base = derive;
base.show(123);
//base.show();
//base.Derive::show();
}
引用访问派生类中由基类继承来的对象,不能访问派生类中的新成员
(3)多重继承
一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表
class 类名 : public 基类1,public 基类2{
};
- 案例一:等腰直角三角形继承等腰三角形与直角三角形
- 注意:多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){} //多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)
- 完整案例
#include <iostream>
#include <cmath>
using namespace std;
class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
float a,b,c;
public:
Triangle(float a,float b,float c):a(a),b(b),c(c){}
float GetLength()const{
return a+b+c;
}
float GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
// int GetA()const{return a;}
};
// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}
float GetBottom()const{
return a;
}
float GetSide()const{
return b;
}
};
class RightTriangle:public Triangle{
public:
RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
float GetArea()const{
cout << "RightTriangle::GetArea()" << endl;
return a*b/2.0;
}
float GetSide()const{
return c;
}
};
class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){} //多重继承的构造函数的写法(利用两个父类都进行构造函数初始化列表)
};
int main(){
cout << sizeof(EqualTriangle) << endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << endl;
cout << e.GetBottom() << endl;
cout << e.GetSide() << endl;
Triangle t(3,4,5);
cout << t.GetArea() << endl;
RightTriangle r(3,4);
cout << r.GetArea() << endl;
EqualRightTriangle er(1);
cout << er.GetCside()<<endl;
cout << er.GetSide()<<endl;
cout << er.GetBottom()<<endl;
}
案例二:多重继承基类构造顺序?
a. 父类在子类中的继承顺序就是对象中父类的构造顺序;
class C:public A,public B{} //a.父类在子类中的继承顺序就是对象中父类的构造顺序;
b. 同一个父类的多个子类,构造顺序为初始化顺序;
class A:public Base{} //b.同一个父类的多个子类,构造顺序为初始化顺序;
class B:public Base{}
- 完整案例
#include <iostream>
using namespace std;
class Base{
public:
Base(){cout << __func__ <<endl;}
~Base(){cout << __func__ <<endl;}
};
class A:public Base{ //b.同一个父类的多个子类,构造顺序为初始化顺序;
public:
A(){cout << __func__ <<endl;}
~A(){cout << __func__ <<endl;}
};
class B:public Base{
public:
B(){cout << __func__ <<endl;}
~B(){cout << __func__ <<endl;}
};
class C:public A,public B{ //a.父类在子类中的继承顺序就是对象中父类的构造顺序;
public:
C(){cout << __func__ <<endl;}
~C(){cout << __func__ <<endl;}
};
int main(){
C c;
}
(4)钻石继承/菱形继承
- 概念:两个子类继承同一个父类,而又有子类同时继承这两个子类。
- 大小:孙类的大小等于父类之和
cout << sizeof(Triangle) << endl; //12
cout << sizeof(EqualTriangle) << endl; //12
cout << sizeof(RightTriangle) << endl; //12
cout << sizeof(EqualRightTriangle) << endl; //24 b.大小:孙类的大小等于父类之和
问题:
- 同名变量如何进行访问(权限符);
注意:变量的顺序为(第二个继承父类的倒序)
cout << "a:" << EqualTriangle::a <<endl; //1.相同变量如何进行访问(权限符);
cout << "b:" << EqualTriangle::b <<endl;
cout << "c:" << EqualTriangle::c <<endl;
cout << "a:" << RightTriangle::a <<endl;//1.相同变量如何进行访问(权限符);倒序
cout << "b:" << RightTriangle::a <<endl;
cout << "c:" << RightTriangle::a <<endl;
- 同名的函数如何进行访问;(权限符)
er.Print();
cout << er.EqualTriangle::GetLength() << endl;
cout << er.RightTriangle::GetLength() << endl; // 同名的函数如何进行访问;(权限符)
- 完整案例
#include <iostream>
#include <cmath>
using namespace std;
class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
float a,b,c;
public:
Triangle(float a,float b,float c):a(a),b(b),c(c){}
float GetLength()const{
return a+b+c;
}
float GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
// int GetA()const{return a;}
};
// EqualTriangle is a Triangle
class EqualTriangle:public Triangle{
public:
EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}
float GetBottom()const{
return a;
}
float GetSide()const{
return b;
}
};
class RightTriangle:public Triangle{
public:
RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
float GetArea()const{
cout << "RightTriangle::GetArea()" << endl;
return a*b/2.0;
}
float GetCside()const{
return c;
}
};
class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right){}
void Print(){
cout << "a:" << EqualTriangle::a <<endl; //c.1相同变量如何进行访问(权限符);
cout << "b:" << EqualTriangle::b <<endl;
cout << "c:" << EqualTriangle::c <<endl;
cout << "a:" << RightTriangle::a <<endl;//c.1相同变量如何进行访问(权限符);倒序
cout << "b:" << RightTriangle::a <<endl;
cout << "c:" << RightTriangle::a <<endl;
}
};
int main(){
cout << sizeof(EqualTriangle) << endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << endl;
cout << e.GetBottom() << endl;
cout << e.GetSide() << endl;
Triangle t(3,4,5);
cout << t.GetArea() << endl;
RightTriangle r(3,4);
cout << r.GetArea() << endl;
EqualRightTriangle er(1);
cout << er.GetCside()<<endl;
cout << er.GetSide()<<endl;
cout << er.GetBottom()<<endl;
cout << sizeof(Triangle) << endl; //12
cout << sizeof(EqualTriangle) << endl; //12
cout << sizeof(RightTriangle) << endl; //12
cout << sizeof(EqualRightTriangle) << endl; //24 b.大小:孙类的大小等于父类之和
er.Print();
cout << er.EqualTriangle::GetLength() << endl;
cout << er.RightTriangle::GetLength() << endl; //c.2 同名的函数如何进行访问;(权限符)
}
解决变量名冲突虚继承
- 虚继承&虚基类
虚继承:在继承定义中包含了 virtual 关键字的继承关系。
虚基类:在虚继承体系中的通过 virtual 继承而来的基类。
class 类名:public virtual 基类{
}
总结
a.虚继承需要将父类的public改为 public virtual;孙类仍为public;
class EqualTriangle:public virtual Triangle{ //a.虚继承需要将父类的public改为 public virtual;孙类仍为public;
b.孙类的构造函数的初始化列表需要加上爷类的初始化;
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right),Triangle(right,right,sqrt(right*right*2)){} //b.孙类的构造函数的初始化列表需要加上爷类的初始化;
c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;
cout << sizeof(Triangle) << endl; //24
cout << sizeof(EqualTriangle) << endl;//24
cout << sizeof(RightTriangle) << endl;//24 c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;
cout << sizeof(EqualRightTriangle) << endl;//32
- 完整案例
#include <iostream>
#include <cmath>
using namespace std;
class Triangle{
//private: // 私有成员子类不能访问
protected: // 保护成员变量可以被子类访问
float a,b,c;
public:
Triangle(float a,float b,float c):a(a),b(b),c(c){}
float GetLength()const{
return a+b+c;
}
float GetArea()const{
float p = (a+b+c)/2.0;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
// int GetA()const{return a;}
};
// EqualTriangle is a Triangle
class EqualTriangle:public virtual Triangle{ //a.虚继承需要将父类的public改为 public virtual;孙类仍为public;
public:
EqualTriangle(float bottom,float side):Triangle(bottom,side,side){}
float GetBottom()const{
return a;
}
float GetSide()const{
return b;
}
};
class RightTriangle: public virtual Triangle{
public:
RightTriangle(float a,float b):Triangle(a,b,sqrt(a*a+b*b)){}
float GetArea()const{
cout << "RightTriangle::GetArea()" << endl;
return a*b/2.0;
}
float GetCside()const{
return c;
}
};
class EqualRightTriangle:public EqualTriangle,public RightTriangle{
public:
EqualRightTriangle(float right):EqualTriangle(sqrt(right*right*2),right),RightTriangle(right,right),Triangle(right,right,sqrt(right*right*2)){} //b.孙类的构造函数的初始化列表需要加上爷类的初始化;
void Print(){
/* cout << "a:" << EqualTriangle::a <<endl;
cout << "b:" << EqualTriangle::b <<endl;
cout << "c:" << EqualTriangle::c <<endl;
cout << "a:" << RightTriangle::a <<endl;
cout << "b:" << RightTriangle::a <<endl;
cout << "c:" << RightTriangle::a <<endl;
*/
cout << "a:" << a <<endl;
cout << "b:" << b <<endl;
cout << "c:" << c <<endl;
}
};
int main(){
cout << sizeof(EqualTriangle) << endl;
EqualTriangle e(10,15);
cout << e.GetLength() << endl;
cout << e.GetArea() << endl;
cout << e.GetBottom() << endl;
cout << e.GetSide() << endl;
Triangle t(3,4,5);
cout << t.GetArea() << endl;
RightTriangle r(3,4);
cout << r.GetArea() << endl;
EqualRightTriangle er(1);
cout << er.GetCside()<<endl;
cout << er.GetSide()<<endl;
cout << er.GetBottom()<<endl;
cout << sizeof(Triangle) << endl; //24
cout << sizeof(EqualTriangle) << endl;//24
cout << sizeof(RightTriangle) << endl;//24 c.大小:因为引入了虚继承,每一次继承需要加一个指针的大小,并填充,孙类则加上继承数*指针大小(8个字节),并填充;
cout << sizeof(EqualRightTriangle) << endl;//32
er.Print();
cout << er.GetLength() << endl;
cout << er.RightTriangle::GetLength() << endl;
}
d.虚继承构造函数的调用次数:爷类仅仅调用一次构造函数;
class Base{ // d.虚继承构造函数的调用次数:爷类仅仅调用一次;
e.空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;
class Empty{}; //e.空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;
- 完整案例
#include <iostream>
using namespace std;
class Base{ // d.虚继承构造函数的调用次数:爷类仅仅调用一次;
public:
Base(){cout << __func__ <<endl;}
~Base(){cout << __func__ <<endl;}
};
class A:public virtual Base{
public:
A(){cout << __func__ <<endl;}
~A(){cout << __func__ <<endl;}
};
class B:public virtual Base{
public:
B(){cout << __func__ <<endl;}
~B(){cout << __func__ <<endl;}
};
class C:public A,public B{
public:
C(){cout << __func__ <<endl;}
~C(){cout << __func__ <<endl;}
};
class Empty{}; //e。空类的大小为1个字节,原因:申请最小的内存,编辑器需要预先留下最小的空间,相当于一个占位符;
int main(){
C c;
cout << sizeof(Base) <<endl;
cout << sizeof(A) <<endl;
cout << sizeof(B) <<endl;
cout << sizeof(C) <<endl;
cout << sizeof(Empty) <<endl; //1
}
虚基类是一个相对概念,在虚继承关系中,父类相对与子类是虚基类。
- 问题:下面B、C继承与A,D同时继承B、C,那么同时D的实例调用A的成员函数会有什么情况?
#include <iostream>
using std::cout;
using std::endl;
class A{
public:
void test();
private:
int id;
};
void A::test(){
cout << __func__ << endl;
}
class B : public A {};
class C : public A {};
class D : public B,public C{};
int main(){
cout << "A size:" << sizeof(A) << endl;
cout << "B size:" << sizeof(B) << endl;
cout << "C size:" << sizeof(C) << endl;
cout << "D size:" << sizeof(D) << endl;
D d;
d.test();
}
- 原因:B与C都继承了A的成员函数test(),D同时继承了B与C,调用test()无法确定是B还是C的。
- 解决:给调用的成员函数前加上访问限定符,明确指定调用成员函数所属的类d.B::test();或者d.C::test();。
派生类的编码
- 构造与析构
- 继承基类成员
- 重载基类成员函数
- 增加新成员
测验
- 写出下列程序的执行结果,并分析结果。(如果程序编译有错,请分析原因,并写出解决方法)
#include <iostream>
using namespace std;
class Base{
public:
Base(){
cout << "Base constuct" << endl;
}
~Base(){
cout << "Base destuct" << endl;
}
};
class Member{
public:
Member(){
cout << "Member constuct" << endl;
}
~Member(){
cout << "Member destuct" << endl;
}
};
class Derive:public Base{
public:
Derive(){
cout << "Derive constuct" << endl;
}
~Derive(){
cout << "Derive destuct" << endl;
}
private:
Member m;
};
int main(){
Derive d;
}
7.(面试)关于多重继承
- 什么是多重继承?同时继承多个父类。
- 多重继承有什么危害?菱形继承/钻石继承。
- 什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类。
- 菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类。所以会有完全相同的属性和方法。因此当前多重继承类有两份相同的属性和方法。使用时会出现冲突。
- 如何解决菱形继承/钻石继承导致的冲突?使用虚继承。
- 什么是虚继承?父类在继承具有相同的祖先类时,加上virtual.
8. 对象构造顺序总结
对象构造顺序
- 基本原则
- 先父后子
- 从左到右
- 先虚后实
- 从上到下
- 由内及外(先构造成员变量,再构造自己)
- 案例
#include <iostream>
using namespace std;
class A{
public:
A(){cout<<__func__<<endl;}
~A(){cout<<__func__<<endl;}
};
class B{
public:
B(){cout<<__func__<<endl;}
~B(){cout<<__func__<<endl;}
};
class I{
public:
I(){cout<<__func__<<endl;}
~I(){cout<<__func__<<endl;}
};
class J{
public:
J(){cout<<__func__<<endl;}
~J(){cout<<__func__<<endl;}
};
class K{
public:
K(){cout<<__func__<<endl;}
~K(){cout<<__func__<<endl;}
};
class C{
public:
C(){cout<<__func__<<endl;}
~C(){cout<<__func__<<endl;}
};
class X{
public:
X(){cout<<__func__<<endl;}
~X(){cout<<__func__<<endl;}
};
class Y{
public:
Y(){cout<<__func__<<endl;}
~Y(){cout<<__func__<<endl;}
};
class Z{
public:
Z(){cout<<__func__<<endl;}
~Z(){cout<<__func__<<endl;}
};
class Simple:public I,public J, public K, public virtual X,public virtual Y,public virtual Z{
private:
A a;
B b;
C c;
public:
Simple(){cout<<__func__<<endl;}
~Simple(){cout<<__func__<<endl;}
};
int main(){
Simple s;
}