C++进阶:继承(三)

147 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

一、继承中的构造函数

✔ 测试用例一:

#include<iostream>
#include<string>
using namespace std;

class Person
{
public:
	//Person(const char* name = "dancebit")
	Person(const char* name)
		: _name(name)
	{
		cout << "Person(const char* name = \"dancebit\")" << endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person& operator=(cconst Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name;
	int a;
};
class Student : public Person
{
public:
	Student(const char* name, int id, const char* address)//推荐
		//: _name(name)//err,父类继承下来是一个整体
		: Person(name)
		, _id(id)
		, _address(address)
	{}
	
	//Student(const char* name, int id, const char* address)//不推荐
	//	: _id(id)//初始化列表阶段会先调用父类的默认构造
	//	, _address(address)
	//{}
private:
	int _id;
	string _address;
};

int main()
{
	//Student s1;
	Student s2("DANCEBIT", 1, "China");

	return 0;
}
  • 对于子类的构造函数,我们不写,编译器会默认生成。它针对 a) 内置类型成员不处理,除非声明时给了缺省值; b) 自定义类型成员,调用它的默认构造函数; c) 继承的父类成员作为一个整体,调用父类的默认构造函数;

  • 父类里写了默认构造函数、子类里没写构造函数,我们只定义了 Studnet 的对象,没有定义 Person 的对象,但是这里却调用了 Person 的构造和析构,这里是子类里默认生成的构造函数调用的,同时也看到了这里设计时没有把继承下来的父类成员混淆到自己的内置和自定义类型成员中。这里继承下来的父类成员会作为一个整体调用它的默认构造函数;内置类型不处理 (除非声明时给了缺省值);自定义类型会调用它的默认构造函数。注意严格来说是先处理父类继承下来的,内置类型和自定义类型可以认为是平等的。

    在这里插入图片描述

    在这里插入图片描述

  • 如果父类没有默认构造函数,那么想对父类的成员进行初始化,使用子类默认生成的构造函数是不行的,因为子类默认生成的构造函数要去调用父类的默认构造函数,而父类没有默认构造函数,所以需要自己实现子类构造函数 (Student s1 + 子类全缺省默认构造函数 || Student s2("DANCEBIT", 1, "China") + 子类全缺省默认构造函数/构造函数)。要注意父类是作为一个整体,调用父类的构造函数初始化,对于构造函数我们自己实现是有价值的。

  • 如果父类使用默认生成的构造函数 (注意测试时需要将拷贝构造一起注释掉,因为拷贝构造也是构造),子类的构造函数不调用父类,当然也调用不了父类,它是在子类的初始化列表中调用的,可以看到父类的 _name 依然能初始化,因为 _name 是 string 类型的,它会去调用 string 的默认构造函数初始化。这里对于编译器默认生成的或无参的构造函数在子类就不能显示的初始化了,但是对于全缺省的依然可以显示的初始化。

  • 如何设计出一个不能被继承的类 ❓

    在这里插入图片描述

      构造函数设计成私有,就可以认为这个类不能被继承了,因为子类要初始化父类继承下来的成员一定要去调用父类的构造函数,而构造函数私有则意味着父类的构造函数在子类中不可见,这里就可以看到 private 还是有点使用的价值的,但也只是在 C++98 中,因为在 C++98 中如果想做到一个不能被继承的类,只能将构造函数私有,但是这样不定义子类对象和不调用父类的构造函数是不会报错的,注意可能是由于编译器检查严格的原因,就算不定义子类对象,在子类中显示的调用了父类的构造函数也会报错,所以你会发现 C++98 这种方式不够彻底和直观。

      在多态中,我们会介绍 C++11 中的关键字 final 用于替代 C++98 中的方式。