Lesson1

继承

现在需要建立 Student 类型和 Teacher 类型。我们发现,两者有相同的地方,比如都有名字,都可以说话。两者也有不同的地方,比如说话的方式不同。我们可以将相同的地方抽象成 Human 类型,让 Student 和 Teacher 去继承 Human,再让两者分别在自己的类型中实现不同的地方。这里,Human 是基类,Student 和 Teacher 是子类:

class Human {
public:
	virtual ~Human() {};
	
	virtual void talk() = 0;
};

class Student final : public Human {
public:
	Student(const std::string& name) : name_(name) {}
	
	void talk() override {
		std::cout << "Student: " << name_ << "\n";
	}
	
private:
	std::string name_;
};

class Teacher final : public Human {
public:
	Teacher(const std::string& name) : name_(name) {}

	void talk() override {
		std::cout << "Teacher: " << name_ << "\n";
	}

private:
	std::string name_;
};

现在需要创建一个 Human。为此,我们编写一个工厂函数,根据输入的类型和名字,返回相应的 Human 对象:

// 用来指明 Human 的类型
enum class Type {
	student, teacher
};

// 返回一个类型是 type,名字是 name 的 Human 对象
Human* make_human(Type type, const std::string& name) {
	if (type == Type::student) {
		return new Student(name);
	}
	else {	// type == Type::teacher
		return new Teacher(name);
	}
}

我们来使用一下:

int main() {

	// *p1 的类型是 Human,实际类型是 Student
	Human* p1 = make_human(Type::student, "mimi");
	p1->talk();	// 输出 Student: mimi

	// *p2 的类型是 Human,实际类型是 Teacher
	Human* p2 = make_human(Type::teacher, "xiao");
	p2->talk();	// 输出 Teacher: xiao
}

这里我们用到了多态,即同一个类型可能有不同的形式。上面的 *p1 和 *p2 都是 Human 类型,但一个表现为 Student,一个表现为 Teacher。

多态是通过虚函数实现的。每个类型都有自己的虚表,里面存放了该类型中每个函数的位置。每个对象都有自己的虚指针,指向实际类型的虚表。在上面的代码中,*p1 的虚指针指向 Student 类型的虚表,调用 talk() 函数时会去 Student 类型的虚表中查找。*p2 也是一样。

注意,只有定义了虚函数的类型(包括从基类继承来的虚函数)才会有虚表。

构造和析构的顺序

先回顾一下 Student 类型:

class Student final : public Human {
public:
	Student(const std::string& name) : name_(name) {}
	
	void talk() override {
		std::cout << "Student: " << name_ << "\n";
	}
	
private:
	std::string name_;
};

构造的顺序:

  • 构造基类(Human)子对象。为什么叫子对象呢?这是因为基类对象是包含在子类对象中的。注意,这时虚指针指向基类的虚表,基类子对象构造完成后,才指向子类的虚表;
  • 构造子类的成员(name_);
  • 执行子类构造函数的函数体。

析构的顺序和构造的顺序相反:

  • 执行子类析构函数的函数体;
  • 析构子类的成员(name_);
  • 析构基类(Human)子对象。

六大函数

每个类都有六大函数,它们与对象的创建和销毁息息相关:

class Human {
public:
	// 默认构造函数
	Human() {}
	// 析构函数
	~Human() {}
	// 拷贝构造函数
	Human(const Human& rhs) {}
	// 拷贝赋值函数
	Human& operator=(const Human& rhs) {}
	// 移动构造函数
	Human(Human&& rhs) {}
	// 移动赋值函数
	Human& operator=(Human&& rhs) {}
};

上面是我们自己动手实现。如果我们不写的话,编译器会帮我们实现:

class Human {
	// 编译器帮我们实现
};

我们也可以显示要求编译器帮我们实现,最后生成的代码和前面两种一样:

class Human {
public:
	Human() = default;
	~Human() = default;
	Human(const Human& rhs) = default;
	Human& operator=(const Human& rhs) = default;
	Human(Human&& rhs) = default;
	Human& operator=(Human&& rhs) = default;
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值