面向对象的核心是:数据抽象、继承、动态绑定
数据抽象:将类的接口与实现分离
继承:对类型的相似关系进行定义建模
动态绑定:忽略相似类的区别,统一使用它们的对象
15.2基类与派生类
class Base { public: Base() = default; Base(int value) :value(value){} virtual int get_value() { return value; }; virtual ~Base() = default; private: int value; protected: void change_value() { value++; } };
基类都应该定义一个虚函数,这样在delete一个指向派生类对象的基类指针所指向new分配的空间时,能会有理想的析构函数调用(先调用派生类的析构函数,再调用基类的。否则将会仅调用基类的析构函数)。
class Derived final:public Base { public: Derived() = default; Derived(int value) :Base(value) {} int get_value() override { return value*value; } };
派生类覆盖基类的虚函数,可以使用(不必须,旧标准没有)override关键字,同时派生类的此函数自动成为virtual的,不管加不加virtual关键字。
final表明这个类不能被继承。
如果需要喜用被override的基类成员,需要加上基类的作用域Base::
15.2.3类型转换与继承
- 派生类的指针或引用可以隐式转换成基类的指针引用,反之则不行。
- 派生类的对象和基类的对象之间不存在期望的类型转换,因为派生类赋值给基类的时候,实际发生的是执行拷贝构造函数等过程,最终得到一个新的基类对象。
15.3虚函数/抽象基类
我们如果想调用被override的虚函数,可以使用作用域运算符进行调用。
如果虚函数=0,就是纯虚函数。有纯虚函数的类是抽象基类。我们不能创建抽象基类的对象。
15.5访问控制与继承
protect
protect成员对类的用户来讲是不可访问的
派生类protect继承基类,则基类的public成员降维成protect成员存在于派生类中
派生类的成员可以通过派生类的对象访问基类的protect成员(因为2中继承过来,相当于自己的protect成员),而不能通过基类的对象访问基类的protect成员(因为1)
private
private成员对用类的用户来讲是不可访问的。private继承过来的基类的所有成员在派生类中都将会是私有的。
派生类public继承的基类之间,才能够进行类型转换
提升基类成员的访问级别
class Base { public: std::size_t size() const { return n; } protected: std::size_t n; }; class Derived : private Base { public: //本来对于使用者是private的,但是提升到了public using Base::size; protected: //本来对于使用者是private的,但是提升到了protected using Base::n; };
15.6继承中类的作用域
在对象进行成员访问的时候,首先从其静态作用域中需要成员。如果找不到,去外层基类中进行查找。
如果派生类定义了和基类中名字相同的成员,则基类的成员在派生类的作用域中将会被隐藏。如果要使用,则可以通过基类的作用域进行调用。
15.7构造函数与拷贝控制
基类为虚析构函数时,使用基类的指针或引用删除(delete)派生类对象时,才能正确执行派生类的析构函数。
基类的虚析构函数(哪怕=default)还会阻止合成的移动操作。(析构函数一般和合成的移动操作有冲突)所以派生类已没有合成的移动操作,一般应该重新定义如下:
class Quote { public: Quote() = default; // memberwise default initialize Quote(const Quote&) = default; // memberwise copy Quote(Quote&&) = default; // memberwise copy Quote&operator=(const Quote&) = default; // copy assign Quote&operator=(Quote&&) = default; // move assign virtual~Quote() = default; // other members as before };
15.7.4继承的构造函数
默认、拷贝、移动构造函数将不会被继承。
class Base { public: Base(int n) { cout << "base" << endl; } }; class Derived final:public Base { public: using Base::Base; //上边这个继承的基类构造函数会被编译器产生如下代码 //但是如果用户自定义了,基类的构造函数就会被覆盖 //Derived(int n) :Base(n) { cout << "derived" << endl; } };