C++学习笔记----9、发现继承的技巧(六)---- 有趣且令人迷惑的继承问题(3)

2.4、数据成员的初始化

        当使用继承构造函数时,确保所有的数据成员被正确地初始化。例如,看下面Base与Derived的新定义。这些定义在所有场景下都没有正确地初始化,你知道的,不推荐没有初始化的数据成员。

class Base
{
public:
	virtual ~Base() = default;
	explicit Base(std::string_view str) : m_str{ str } {}
private:
	std::string m_str;
};

class Derived : public Base
{
public:
	using Base::Base;
	explicit Derived(int i) : Base{ "" }, m_int{ i } {}
private:
	int m_int;
};

可以生成一个Derived对象如下:

Derived s1{ 2 };

        这会调用Derived(int)构造函数,它会初始化Derived类的m_int数据成员,调用带有空的字符串的Base构造函数来初始化m_str数据成员。

        因为Base构造函数继承了Derived类,也可以构建Derived对象如下:

Derived s2 { "Hello World" };

        它调用了Derived中的继承Base的构造函数。然而,该继承Base构造函数只是初始化了Base类的m_str,没有初始化Derived类的m_int,处于未初始化状态。这也是不推荐的。

        这种情况的解决方案是使用内联类成员初始化器,我们在前面的章节讨论过。下面的代码使用了内联类初始化器初始化m_int为0。当然了,Derived(int)构造函数仍然可以改变它,把m_int初始化为构造函数的参数i。

class Derived : public Base
{
public:
	using Base::Base;
	explicit Derived(int i) : Base{ "" }, m_int{ i } {}
private:
	int m_int{ 0 };
};

3、重载成员函数的特殊场景

        当重载一个成员函数时,有些特殊场景需要注意。本节列出你可能碰到的几种场景。

3.1、基类成员函数为静态

        在c++中,不能重载静态成员函数。大部分情况,知道这些就足够了。但是,有几个推论需要理解一下。

        首先,成员函数不能同时为static和virtual。这是尝试重载static成员函数得不到期待的结果的第一个线索。如果有一个继承类中的static成员函数与基类中的static成员函数一样,实际上拥有的是两个独立的成员函数。

下面的代码展示了两个类,都碰巧有叫做beStatic()的static成员函数。这两个成员函数丝毫相关性也没有。

class BaseStatic
{
public:
	static void beStatic() {
		println("BaseStatic being static.");
	}
};

class DerivedStatic : public BaseStatic
{
public:
	static void beStatic() {
		println("DerivedStatic keepin' it static.");
	}
};

        因为static成员函数属于它的类,在两个不同的类上调用同样名字的成员函数实际上调用的是各自的成员函数。

BaseStatic::beStatic();
DerivedStatic::beStatic();

        输出如下:

BaseStatic being static.
DerivedStatic keepin' it static.

        只要成员函数被类名字访问就一切万事大吉。当对象介入时,其行为就不那么清晰了。在c++中,可以使用一个对象调用一个static成员函数,但是因为成员函数是static,它没有this指针,也不能访问对象自身,所以与用类名调用是等价的。还是前面的那个例子,可以写如下的代码,但是结果可能是令人惊讶的。

		DerivedStatic myDerivedStatic;
		BaseStatic& ref{ myDerivedStatic };
		myDerivedStatic.beStatic();
		ref.beStatic();

        第一个对beStatic()的调用很明显调用的是DerivedStatic版本,因为它是在声明为DerivedStatic的对象上显式调用的。第二个调用可能就不是你想要的结果了。对象是一个BaseStatic引用,但是它指向了一个DerivedStatic对象。在这种情况下,BaseStatic的beStatic()版本被调用。原因是当调用static成员函数时,c++不关心实际上是什么对象。它只关心编译时的类型。在这种情况下,类型是指向BaseStatic的引用。

        上面例子的输出如下:

DerivedStatic keepin' it static.
BaseStatic being static.

        注意:static成员函数用它被定义的类的名字圈定,但是它们不是应用于特定对象的成员函数。当调用一个static的成员函数时,被正常名字解析决定的版本被调用。当使用对象同步调用成员函数时,调用中实际上并没有包含对象,除非是在编译时决定了类型。

猜你喜欢

转载自blog.csdn.net/weixin_71738303/article/details/143278686