继承

继承语法

class 子类名:继承方式1 基类1,继承方式2 基类2…{
     …
};

继承方式

公有继承:public
保护继承:protected
私有继承:private
继承方式基类public成员基类protected成员基类private成员
public       publicprotectedprivate
protectedprotectedprotectedprivate
privateprivateprivateprivate

这张表格里面公有继承,基类的访问控制权限不变;

保护继承,基类的访问控制权限最低为protected;

私有继承,基类的访问控制权限全部为private.

公有继承特点
  • 子类可以直接访问基类的所有公有和保护成员,其效果如同它们是在子类中声明一样
  • 对于基类的私有成员,在子类中存在但不能访问
  • 在子类中定义基类中同名的公有成员或保护成员,子类中的成员会隐藏基类同名成员,想访问被隐藏的成员,可以借助作用域限定符“::”
  • 子类对象任何时候都可以被当成基类类型对象(皆然性   ISA)
保护继承特点
  • 使基类公有成员和保护成员进行保护化,只禁止外部通过该子类访问
  • 子类指针或引用不能隐式转换成基类类型指针或引用
私有继承特点
  • 将基类公有和保护成员私有化,禁止外部通过该子类访问,也禁止该子类的子类访问
  • 子类指针或引用不能隐式转换成基类类型指针或引用

访问控制权限

访问控制限定符访问控制属性基类子类外部友元
public公有成员OKOKOKOK
protected保护成员OKOKNOOK
private私有成员OKNONOOK
  • 不写继承方式,默认私有继承
  • 继承方式是用来指明基类成员在派生类中的最高访问权限的
  • 使用 using关键字可以改变基类公有和保护成员在派生类中的访问权限

继承演示

#include <string>
#include <iostream>
using namespace std;
class Human
{

public:
Human()
{
cout << "Human  构造:" << this << endl;
}
void sleep(){
cout << "基类 Sleep" << endl;
}
private:
int m_age;
protected:
string m_name;
public:
bool m_gender;  //0 女  1 男
int m_show;
};  //基类 父类

class Student : public Human
{
public:
Student() : m_id(0)
{
/*
* 1.派生类可以直接访问基类的所有公有和保护成员,其效果如同它们是在派生类中声明一样
*/


//基类中的保护成员在派生类中可以访问
m_name = "梦凡";
//基类中的公有成员在派生类中可以访问
m_gender = 1;

/*
* 2.对于基类的私有成员,在派生类中存在但不能访问
*/

//m_age = 10;
}
void show()
{
//隐藏基类中的m_show变量 基类中的m_show存在,用作用域限定符访问
cout << "派生类中的 m_show:" << m_show <<" "<< "基类中的m_show:" << Human::m_show << endl;
}
private:
int m_id;
public:
// 3.同名隐藏  派生类中定义基类中同名的公有成员或保护成员(成员包括成员变量和成员函数)
int m_show; //隐藏基类中的m_show变量 基类中的m_show存在,用作用域限定符访问
int sleep; //隐藏基类中的sleep()函数
}; //派生类 子类

int main()
{
Student s;
/*
* 3.同名隐藏测试
*/

//s.sleep("水果"); //会报错 因为派生类中的sleep变量将父类中的sleep函数隐藏了
s.m_show = 10; //m_show为派生类中的m_show
s.Human::m_show = 20; //通过作用域访问基类中的m_show
s.show();

/*
* 4.公有继承 派生类对象任何时候都可以被当成基类类型对象
* 基类对象不能隐式转换成派生类对象
*/

//缩小访问范围 安全
Human h = s;
h.sleep(); //不报错,因为派生类对象隐式转换成基类对象

Human* ph = &s; //基类指针指向派生类对象 ph隐式指向基类部分
Human& rh = s; //基类引用指向派生类对象 rh隐式指向基类部分

cin.get();
return 0;
}

阻断继承

将基类构造函数设为私有,子类永远无法被实例化对象,实现阻断继承

//阻断继承 将构造函数声明为私有
class parent
{

private:
parent() {}
};
class son :public parent
{
};
int main()
{
   //son s; 无法实例化对象
}

构造析构顺序

子类构造
  • 子类构造函数会调用基类构造函数,构造子类对象中的基类子对象
  • 子类构造函数没有显示指明基类构造方式,会选择基类的缺省构造函数(无参构造)
  • 子类显示调用基类构造函数可以在初始化表中显示指明构造方式。
  • 构造过程:构造基类子对象-》构造成员变量-》执行构造代码
子类析构
  • 子类析构会调用基类析构
  • 通过基类指针析构子类对象,只会析构子类对象中的基类子对象。可能造成内存泄漏
  • 析构过程:执行析构代码-》析构成员变量-》析构基类子对象
演示
#include <string>
#include <iostream>
using namespace std;
class Human
{

public:
Human()
{
cout << "Human构造 this:" << this << endl;
}
Human(int age)
{
cout << "Human单参 this:" << this << endl;
}
private:
int m_age;
string m_name;
int m_gender;
};  //基类

class Student : public Human
{
public:
//默认调用基类无参构造
//Student() :m_no(0) {
// cout << "Student构造 this:" << this << endl;
//}

/*
* 2.通过初始化列表选择基类的构造方式
*/

Student(int age) :Human(age), m_no(0)
{
std::cout << "Student单参构造 this:" << this << std::endl;
}
private:
int m_no;
};

int main()
{
/*
* 1.构造顺序 在构造析构里面分析过
* 为整个对象分配内存
* 构造基类部分(如果存在基类)
* 构造成员变量
* 执行构造函数代码
*/

//无参构造
//Student s1;
//单参构造
Student s1(18);
cin.get();
return 0;
}

多继承

一个类可以从多个基类继承

多重继承内存布局

子类对象中的基类子对象按照继承表顺序依次构造

#include <iostream>
using namespace std;
class Telephone
{

public:
Telephone()
{
cout << "Telephone构造" << endl;
}
void call()
{
cout << "打电话" << endl;
}
int m_t;
};

class Camera
{

public:
Camera()
{
cout << "Camera构造" << endl;
}
void photo()
{
cout << "照相" << endl;
}
int m_c;
};

class Ipod
{

public:
Ipod()
{
cout << "Ipod构造" << endl;
}
void music()
{
cout << "听歌" << endl;
}
int m_i;
};

//子类对象中的基类子对象按照继承表顺序依次构造
class IphoneXs :public Telephone, public Camera, public Ipod
{
public:
IphoneXs()
{
cout << "IphoneXs构造" << endl;
}
int i;
};

int main()
{
IphoneXs ipx;   //构造一个子类对象
ipx.call();
ipx.photo();
ipx.music();
cout << "------------------" << endl;

//基类指针指向子类对象
Telephone* t = &ipx;  //公有继承 皆然性   子类IS A父类
Camera* c = &ipx;      //公有继承 皆然性   子类IS A父类
Ipod* i = &ipx;  //公有继承 皆然性   子类IS A父类

cout << t << " " << c << " " << i << endl;
cin.get();
return 0;
}
Telephone构造
Camera构造
Ipod构造
IphoneXs构造
打电话
照相
听歌
------------------
006FF754 006FF758 006FF75C

棱形继承

继承_Python


中间子类有公共基类,导致最终子类存在多个公共基类,导致调用歧义通过使用::明确调用

#include <iostream>
using namespace std;
class A
{

public:
A()
{
cout << "A构造" << this << endl;
}
int m_a;
void foo()
{
m_a = 10;
cout << m_a << endl;
}
};

class X :public A
{
public:
X()
{
cout << "X构造" << this << endl;
}
};

class Y :public A
{
public:
Y()
{
cout << "Y构造" << this << endl;
}
};

class Z :public X, public Y
{
public:
Z()
{
cout << "Z构造" << this << endl;
}
};

int main()
{
Z z;
//z.foo();   调用不明确   z对象中有X和Y的对象,X和Y中有A的对象
//不知道调用X->A.foo 还是调用Y->A.foo();
z.X::foo();
z.Y::foo();

//z.A::foo();
z.X::A::foo();
return 0;
}

虚继承

在公共基类继承方式前加关键字virtual

在公共基类对象保存到公共位置,存储偏移值

继承_Python_02

虚继承是为了解决棱形继承中成员访问的二义性。在A B继承方式前加关键字virtual,编译器将Base的数据保存在一个公共位置,通过虚基表访问。

#include <iostream>
using namespace std;
class A
{

public:
A()
{
cout << "A构造" << this << endl;
}
int m_a;
int m_b;
void foo()
{
m_a = 10;
cout << m_a << endl;
}
};
/*
* 解决方法二:虚继承基类A
*/

class X :virtual public A
{
public:
X()
{
cout << "X构造" << this << endl;
}
};

class Y :virtual public A
{
public:
Y()
{
cout << "Y构造" << this << endl;
}
};

class Z :public X, public Y
{
public:
Z()
{
cout << "Z构造" << this << endl;
}
};

int main()
{
   cout << "A:" << sizeof(A) << ",X:" << sizeof(X) << ",Y:" << sizeof(Y) << ",Z:" << sizeof(Z) << endl;
Z z;
//z.foo(); //对“foo”的访问不明确
/*
* 解决方法一:作用域::这么调用哪个父类里面的foo函数
*/

//z.X::foo();
//z.Y::foo();
z.foo(); //虚继承解决棱形继承问题
cin.get();
return 0;
}
A:4,X:8,Y:8,Z:12
A构造00F6F994
X构造00F6F98C
Y构造00F6F990
Z构造00F6F98C
10