C++11学习笔记之3——类的继承

  1. 禁止继承

在C++11之前,C++没有final关键字,禁止继承的方法大多是利用从虚基类派生的方式。现在final关键字已经被C++11标准收录。

1
2
3
4
5
6
7
8
class BaseClass final
{
...
};

class DerivedClass : public BaseClass // compling error
{
};
  1. 禁止重写(override

和禁止继承一样,通过final关键字来实现,禁止重写中final关键字只能用于virtual成员函数。注意这里指的不是overwrite(覆写),overwrite成员函数可以不是虚函数(参数也可以不同)。示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BaseClass
{
public:
BaseClass();
virtual void someMethod() final;
};

class DerivedClass : public BaseClass
{
public:
DerivedClass();
virtual void someMethod(); // compling error
virtual void someMethod(int i); // overwrite, that`s OK
};
  1. override关键字

同样C++11也引入了大家所期盼的override关键字,用来声明子类重写了基类的某个虚函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
class BaseClass
{
public:
BaseClass();
virtual void someMethod(int i);
};

class DerivedClass : public BaseClass
{
public:
DerivedClass();
virtual void someMethod(int i) override;
};

个人认为这个关键字主要用于防止基类修改了虚函数的参数列表,而导致子类无意识地创建了一个新的虚函数的行为。

  1. 继承构造函数

这个比较有意思,C++11之前,子类不能通过基类的构造函数来创建子类的实例,现在,子类通过using声明可以将基类的构造函数声明为可见,从而可以利用和基类相同的构造函数方式来创建自定义的子类形态,这种方法将来或许大有可为,但是也有需要注意的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class BaseClass
{
public:
BaseClass(const std::string& str); // not implement here
BaseClass(double d); // not implement here
};

class DerivedClass : public BaseClass
{
public:
using BaseClass::BaseClass;
// All baseclass`s constructors are visible
// 继承了所有父类的构造函数

// 自身的构造函数
DerivedClass(int id) : m_nID(id){ }

// 如果派生类中具有和基类的某个构造函数相同的参数,
// 那么优先级会比相同参数的基类构造函数高,将会导致覆盖基类的构造函数
// DerivedClass(const std::string& str);

private:
int m_nID; // using "int m_nID = 0" instead
};

这样使用using声明后,就可以使用double参数版本(或是string&类型参数的版本)的基类构造函数来创建子类对象。但是需要面对子类对象成员初始化的问题:

1
DerivedClass  dcFromBaseCtor("Not good"); // m_nID doesn`t initialize

这样构造出的子类对象无法自定义其自身成员变量的初始化,简单的成员变量可以使用声明初始化来解决这个问题,例如int m_nID = 0;
面对复杂的子类,可以在基类中定义一个初始化的虚函数,由子类重写它来实现子类成员的初始化。由于手头边的VS2012编译器目前还不支持继承构造函数,所以无法实践,但是个人认为下面这么写也是符合C++11标准的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BaseClass
{
public:
BaseClass(std::string& str)
{
m_baseData = str;
Initialize();
}

// do nothing
virtual void Initialize(){}

protected:
std::string m_baseData;
};

class DerivedClass : public BaseClass
{
public:
using BaseClass::BaseClass;

DerivedClass(int value)
:BaseClass(std::string("from dervied")),
m_derivedData(value)
{}

virtual void Initialize() override
{
m_derivedData = 0;
}
int m_derivedData;
};

这时候使用基类的构造函数来初始化子类对象就更安全了:

1
2
std::string str("PasstoBaseClass");
DerivedClass dcFromBaseCtor(str);