目录
继承
通过继承联系在一起的类构成一种层次关系。通常在层次关系的根部有一个基类(父类),其他类则直接或间接地从基类继承而来,这些继承得到的类称 为派生类(子类)。基类负责定义在层次关系中所有类共同拥有的成员,而每个派生类定义各自特有的成员。
派生类必须通过使用类派生列表明确指出它是从哪个(哪些)基类 继承而来的。类派生列表的形式是:首先是一个冒号,后面紧跟以逗号分隔的基类列表, 其中每个基类前面可以有访问说明符:
class Bulk_quote : public Quote { // Bulk_quote
继承了
Quote
派生类构造函数
尽管在派生类对象中含有从基类继承而来的成员,但是派生类并不能直接初始化这些 成员。和其他创建了基类对象的代码一样,派生类也必须使用基类的构造函数来初始化它的基类部分。
每个类控制它自己的成员初始化过程
首先初始化基类的部分,然后按照声明的顺序依次初始化派生类的成员。
class animal
{
public:
animal(std::string);
protected:
std::string name;
};
class dog :public animal
{
public:
dog(std::string s,double d):animal(s),price(d){}
private:
double price;
};
派生类析构函数
如前所述,在析构函数体执行完成后,对象的成员会被隐式销毁。类似的, 对象的基类部分也是隐式销毁的。因此, 和构造函数及赋值运算符不同的是,派生类析构函数只负责销毁由派生类自己分配的资源:
class D : public Base {
public:
//Base::~Base 被自动调用执行
~D() {
/*该处由用户定义清除派生类成员的操作*/
}
};
对象销毁的顺序正好与其创建的顺序相反:派生类析构函数首先执行,然后是基类的析构函数, 以此类推,沿着继承体系的反方向直至最后。
防止继承的发生
有时我们会定义这样一种类,我们不希望其他类继承它,或者不想考虑它是否适合作为一个基类。为了实现这一目的,C++11新标准提供了一种防止继承发生的方法,即在类名后跟一个关键字final:
class NoDerived final {/**/ }; // NoDerived不能作为基类
class Base { /**/ };
// Last是final的;我们不能继承Last
class Last final : Base { /* */ };// Last不能作为基类
class Bad : NoDerived{ /* */ };//错误:NoDerived 是 final 的
class Bad2 : Last { /* */ }; //错误:Last是final的
访问等级
名字冲突与继承
和其他作用域一样,派生类也能重用定义在其直接基类或间接基类中的名字,此时定 义在内层作用域(即派生类)的名字将隐藏定义在外层作用域(即基类)的名字
派生类的成员将隐藏同名的基类成员.
class base
{
public:
void test() { std::cout << "base" << std::endl; }
};
class derived :public base
{
public:
void test() { std::cout << "derived" << std::endl; }
};
void f2()
{
derived d;
d.test();//打印derived
}
通过作用域运算符来使用隐藏的成员
我们可以通过作用域运算符来使用一个被隐藏的基类成员:
class derived :public base
{
public:
void test() { std::cout << "derived" << std::endl; }
void get_base_test() { base::test(); }
};
作用域运算符将覆盖掉原有的查找规则,并指示编译器从Base类的作用域开始查找test。
除了覆盖继承而来的虚函数之外,派生类最好不要重用其他定义在基类中的名字
通过using让父类的同名函数重载
class base
{
public:
void test() { std::cout << "base::test()" << std::endl; }
//与子类冲突,无法通过重载的方式使用
void test(int) { std::cout << "base::test(int)" << std::endl; }
};
class derived :public base
{
//using base::test;//放在外面相当于把base::test写成private的
public:
void test() { std::cout << "derived::test()" << std::endl; }
using base::test;//相当于函数重载,把父类的test重载进来
};
void f4()
{
derived d;
d.test();//调用子类的test()
d.test(1);//调用基类重载进来的test(int)
}
- 这种方式只能指定函数名,无法让父类的一部分函数重载进来
- 这种方式可以实现在子类中调用父类的重载函数
参考书籍:
C++ Primer中文版 第5版作 者: (美)李普曼(Lippman,S.B.),(美)拉乔伊(Lajoie,J.),(美)默(Moo,B.E.) 著 王刚,杨巨峰 译,出版社: 电子工业出版社,ISBN: 9787121155352
本文是学习过程中参照C++primer结合自己的理解所写的笔记,如有纰漏还请指出,谢谢