目录
一、概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
二、格式
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n
实例
#include <iostream>
#include <string>
using namespace std;
//父类
class Animal
{
public:
void eat()
{
cout << "It's eating." << endl;
}
};
//子类
class Dog : public Animal
{
public:
void sleep()
{
cout << "It's sleeping." << endl;
}
};
void test01()
{
Dog dog;
dog.eat();
}
int main()
{
test01();
}
总结:
继承的好处:可以减少重复的代码
class A : public B:
A类称为子类 或派生类
B 类称为父类或基类
派生类中的成员,包含两大部分:
类是从基类继承过来的,一类是自己增加的成员。
从基类继承过过来的表现其共性,而新增的成员体现了其个性
三、继承方式
访问控制表示派生类对基类的继承方式:
public 公有继承
private 私有继承
protected 保护继承
四、继承中的对象模型
#include <iostream>
#include <string>
using namespace std;
//父类
class Base
{
public:
int m_A;
Base()
{
m_A = 100;
m_a = 100;
}
private:
int m_a;
};
//子类
class Son : public Base
{
public:
int m_B;
Son()
{
m_B = 200;
}
};
void test01()
{
Son son;
cout << "son的大小:"<<sizeof(son) << endl;
}
int main()
{
test01();
}
上述实例运行发现,从父类继承过来的成员,都属于子类对象中,私有的也会包含进来。
五、构造和析构顺序
实例
#include <iostream>
#include <string>
using namespace std;
//父类
class Base
{
public:
int m_A;
Base()
{
m_A = 100;
m_a = 100;
std::cout << "父类构造调用"<< endl;
}
~Base()
{
std::cout << "父类析构调用" << endl;
}
private:
int m_a;
};
//子类
class Son : public Base
{
public:
int m_B;
Son()
{
m_B = 200;
std::cout << "子类构造调用" << endl;
}
~Son()
{
std::cout << "子类析构调用" << endl;
}
};
void test01()
{
Son son;
cout << "son的大小:"<<sizeof(son) << endl;
}
int main()
{
test01();
}
运行输出:
父类构造调用
子类构造调用
son的大小:12
子类析构调用
父类析构调用
从上述实例看出:
继承中先调用父类构造函数再调用子类的构造函数析构顺序与构造的顺序相反。
六、继承同名成员处理
访问子类的同名成员,直接访问即可
访问父类的同名成员,需要加作用域
实例
#include <iostream>
#include <string>
using namespace std;
//父类
class Base
{
public:
int m_A;
static int m_B;
Base()
{
m_A = 100;
m_B = 100;
}
};
//子类
class Son : public Base
{
public:
int m_A;
static int m_B;
Son()
{
m_A = 200;
m_B = 200;
}
};
void test01()
{
Son son;
std::cout << "son的m_A:" << son.m_A << endl;
std::cout << "Base的m_A:" << son.Base::m_A << endl;
//静态
//对象访问
std::cout << "son的m_B:" << son.m_B<< endl;
std::cout << "Base的m_B:" << son.Base::m_B << endl;
//类访问
std::cout << "son的m_B:" <<Son::m_B << endl;
std::cout << "Base的m_B:" <<Son::Base::m_B << endl;
}
int main()
{
test01();
}
从上述实例中,看到非静态的和静态的访问都是一样。
七、复杂的菱形继承和菱形虚拟继承
多继承:一个子类有两个或者两个以上的的基类
菱形继承:多继承的特殊情况,有一个共同的基类。
菱形继承的两个问题:
菱形继承会导致数据冗余,导致数据多了。
菱形继承导致数据具有二义性,不知道访问的是哪个成员。需要指明类域。
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
int _a;
A()
{
_a = 10;
}
};
class B : public A
{
public:
int _b;
B()
{
_b = 20;
}
};
class C :public A
{
public:
int _c;
C()
{
_c = 30;
}
};
class D :public B, public C
{
public:
int _d;
};
int main()
{
D d;
std::cout << sizeof(d) << endl;
std::cout << d.C::_a << endl;
//std::cout << d._a << endl;//报错
system("pause");
return 0;
}
解决办法
虚拟继承,是用关键字virtual修饰中间类。
#include <iostream>
#include <string>
using namespace std;
class A
{
public:
int _a;
A()
{
_a=10;
}
};
class B:virtual public A
//class B : public A
{
public:
int _b;
B()
{
_b = 20;
}
};
class C :virtual public A
//class C :public A
{
public:
int _c;
C()
{
_c = 30;
}
};
class D :public B, public C
{
public:
int _d;
};
int main()
{
D d;
std::cout << sizeof(d) << endl;
std::cout << d.C::_a << endl;
std::cout << d._a << endl;
system("pause");
return 0;
}
B继承A,实际A的成员保存在最后面。B类对象中,还保存了一个指针,指针指向一个虚基表。虚基表里保存的是指针位置到实际保存A成员中第一个成员位置的偏移量。
C继承A,实际A的成员保存在最后面。C类对象中,还保存了一个指针,指针指向一个虚基表。虚基表里保存的是指针位置到实际保存A成员中第一个成员位置的偏移量。
D继承B和C,首先会继承B和C属于自己的成员,还会继承B和C的指针(但是指针变量内容不一样,因为虚基表不一样)。两指针指向的是实际保存各自指针位置到A成员的第一个成员的位置的偏移量。
共同基类A的成员保存在最下面。
共同基类的内容被继承下来了,也占据对象的空间。只是保存在最后,并且是一份(解决数据二义性和冗余问题)。