标签: C++Primer 学习记录 继承 派生 虚函数
override
。
class NoDerived final { /* */ }; // NoDerived不能被继承
class Bad : NoDerived { /* */ }; // 错误
Quote base;
Bulk_quote *bulkP = &base; // 错误
Bulk_quote bulk;
Quote *itemP = &bulk; // 正确,动态类型是 Bulk_quote
Bulk_quote *bulkP = itemP; // 错误,不能将基类转换成派生类
override
标识符。而如果被其标识的函数被编译器认为并不能成功覆盖掉基类的虚函数时,则会报错。
struct B { virtual void f1(int) const; virtual void f2(); void f3(); }; struct D1 : B { void f1(int) const override; // 正确 void f2(int) override; // 错误,B没有形如 f2(int)的函数 virtual void f2(int); // 正确,在 D1中声明 f2(int)的虚函数 void f3() override; // 错误,f3不是虚函数 void f4() override; // 错误,B中没有名为 f4的函数 };
final
,则之后任何尝试覆盖该函数的操作都将引发错误。另外,final
和override
说明符要出现在形参列表(包括任何 const或引用修饰符)以及尾后返回类型之后。
struct D2 : B { // 从 B继承 f2()和 f3(),覆盖 f1(int) void f1(int) const final; // 不允许后续的其他类覆盖 f1(int) }; struct D3 : D2 { void f2(); // 正确 void f1(int) const; // 错误 };
=0
,将一个虚函数说明为纯虚函数。其中,=0
只能出现在类内部的虚函数声明语句处。
class A { public: A() = default; int get_size() const = 0; };
using
声明语句,我们可以将该类的直接或间接基类中的任何可访问成员(非私有成员)标记出来。using声明语句中名字的访问权限由该 using声明语句之前的访问说明符来决定。
class Base { public: std::size_t size() const { return n; } protected: std::size_t n; }; // private继承,继承成员的访问权限发生了改变 class Derived : private Base { public: using Base::size; protected: using Base::n; };
class Base { public: virtual void fcn() { cout << "Base::fcn()" << endl; } }; class D1 : public Base { public: // 隐藏基类的 fcn,这个 fcn不是虚函数 // D1继承了 Base::fcn()的定义 void fcn(int); // 形参列表与 Base中的 fcn不一致 virtual void f2(); // 一个新的虚函数,在 Base中不存在 }; class D2 : public D1 { public: void fcn(int); // 非虚函数,隐藏了 D1::fcn(int) void fcn(); // 覆盖了 Base中的虚函数 fcn void f2(); // 覆盖了 D1中的虚函数 f2 }; Base bobj; D1 d1obj; D2 d2obj; // 指向不同对象的基类指针都调用 fcn() Base *bp1 = &bobj; Base *bp2 = &d1obj; Base *bp3 = &d2obj; bp1->fcn(); // 虚调用,将在运行时调用 Base::fcn() bp2->fcn(); // 虚调用,将在运行时调用 Base::fcn() bp3->fcn(); // 虚调用,将在运行时调用 D2::fcn() // 静态指针类型与其动态类型相同,都调用 f2() D1 *d1p = &d1obj; D2 *d2p = &d2obj; bp2->f2(); // 错误,Base中没有名为 f2的函数 d1p->f2(); // 虚调用,将在运行时调用 D1::f2() d2p->f2(); // 虚调用,将在运行时调用 D2::f2() // 不同指针类型都指向 D2对象,都调用 fcn(int) Base *p1 = &d2obj; D1 *p2 = &d2obj; D2 *p3 = &d2obj; p1->fcn(42); // 错误,Base中没有接受一个 int的 fcn p2->fcn(42); // 静态绑定,调用 D1::fcn(int) p3->fcn(42); // 静态绑定,调用 D1::fcn(int)
=default
请求一个移动操作时,如果基类中的对应操作是删除的或不可访问的,那么派生类中该函数将是被删除的,原因是派生类对象的基类部分不可移动。同样,如果基类的析构函数是删除的或不可访问的,则派生类的移动构造函数也将是被删除的。class B { public: B(); B(const B&) = default; // 其他成员,不含有移动构造函数 }; class D { // 没有声明任何构造函数 }; D d; // 正确,D的合成默认构造函数使用 B的默认构造函数 D d2(d); // 错误,D的合成拷贝构造函数是被删除的 D d3(std::move(d)); // 错误,隐式地使用 D的被删除的拷贝构造函数
using
声明语句,派生类可以从基类继承构造函数。通常情况下,using声明语句只是令某个名字在当前作用域内可见,而当作用于构造函数时,using声明语句将令编译器产生代码。只对基类部分进行初始化,派生类自己的数据成员将会默认初始化。
class Bulk_quote : public Disc_quote { public: using Disc_quote::Disc_quote; // 继承 Disc_quote的构造函数 double net_price(std::size_t) const; };