正如前面的博客里写的,C++支持程序员写出在创建或者销毁一个对象时自动调用的方法,也就是构造器和析构器。在没有继承机制的情况下,我们很容易理解这些方法在创建或销毁一个对象的时候被调用,但是一旦使用了继承机制,构造器和析构器就变得有点复杂了。
构造器
比如有个基类Animal(),他将在创建子类Pig类型的对象的时候最先被调用,如果Pig类也有一个构造器,他将排在第二个被调用,因为有一个原则:基类必须在子类之前初始化。
然后继续讨论:如果构造器带着输入参数,事情就变得稍微有点复杂。正确做法如下:
class Animal{
public:
std::string name;
Animal(std::string theName);
};
class Pig:public Animal{
Pig(std::string theName);
};
Animal::Animal(std::string theName){
this->name = theName;
}
Pig::Pig(std::string theName):Animal(theName){
}
注意在子类的构造器定义里的“Animal(theName)”的语法含义是:
当调用Pig()构造器的时候(以theName作为输入参数),Animal()构造器也将被调用(theName输入参数将传递给他)
于是,当我们使用Pig pig("小香猪")的时候,将把字符串“小香猪”传递给Pig()和Animal(),赋值动作实际发生在Animal()方法里,Pig()的构造器是空的,他继承的是Animal()的构造器。
下面就结合上一个博客里面的东西来写一下继承机制下的构造器,注意下面代码里pig类和turtle类的构造器是如何继承animal类的。
#include<iostream>
#include<string>
class Animal{//类名
public:
std::string name;
std::string mouth;
void eat();
void sleep();
Animal(std::string theName);
};
class Pig:public Animal{
public:
void climb();
Pig(std::string theName);
};
class Turtle:public Animal{
public:
void swim();
Turtle(std::string theName);
};
Animal::Animal(std::string theName){
this->name = theName;
}
Pig::Pig(std::string theName):Animal(theName){
}
Turtle::Turtle(std::string theName):Animal(theName){
}
void Animal::eat(){
std::cout<<"I'm eating!\n"<<std::endl;
}
void Animal::sleep(){
std::cout<<"I'm sleeping!Don't disturb me!\n"<<std::endl;
}
void Pig::climb(){
std::cout<<"I can climb the tree!\n"<<std::endl;
}
void Turtle::swim(){
std::cout<<"I can swim!\n"<<std::endl;
}
int main(){
Pig pig("小香猪");
Turtle turtle("小甲鱼");
std::cout<<"我是一只猪,我的名字叫"<<pig.name<<std::endl;
std::cout<<"每只乌龟都有一个伟大的名字"<<turtle.name<<std::endl;
pig.eat();
turtle.eat();
pig.climb();
turtle.swim();
return 0;
}
结果如图:
析构器
在销毁某个对象的时候,基类的析构器也将被自动调用,但是这些事情编译器会自动替你处理。因为析构器不需要输入参数,所以根本用不着使用:SuperClassMethod(arguments)语法。与构造器的情况相反,基类的析构器将在子类的最后一条语句执行完毕后才被调用。
因为在销毁对象时,编译器会帮我们做很多工作,所以我们这一部分就跳过,但是要明白继承的机制在构造器和析构器里是如何体现的,读下面这段代码,写出输出内容,答案放在最后,看是否已经对这个概念有了进一步的掌握。
#include<iostream>
#include<string>
class BaseClass{
public:
BaseClass();
~BaseClass();
void doSomething();
};
class SubClass:public BaseClass{
public:
SubClass();
~SubClass();
};
BaseClass::BaseClass(){
std::cout<<"进入基类构造器....."<<std::endl;
std::cout<<"我在基类构造器里做了一些事...\n"<<std::endl;
}
BaseClass::~BaseClass(){
std::cout<<"\n进入基类析构器....."<<std::endl;
std::cout<<"我在基类析构器里也做了一些事...\n"<<std::endl;
}
void BaseClass::doSomething(){
std::cout<<"\n我做了一些事!\n";
}
SubClass::SubClass(){
std::cout<<"进入子类构造器....."<<std::endl;
std::cout<<"我在子类构造器里做了一些事...\n"<<std::endl;
}
SubClass::~SubClass(){
std::cout<<"进入子类析构器....."<<std::endl;
std::cout<<"我在子类析构器里也做了一些事..."<<std::endl;
}
int main(){
SubClass subclass;
subclass.doSomething();
std::cout<<"OK!我执行完了 要收工了 执行main的return啦~~~\n\n";
return 0;
}
结果:
解析:
其实继承就相当于一个嵌套,父类把子类给嵌套起来,自然先执行父类的构造函数,然后子类的构造函数,然后执行main里调用的函数doSomething(),然后继续执行main函数的下一条语句“OK.......”,析构函数是在执行main的返回值那一行的时候调用的,在那个时候,程序关闭,已经创建的对象要将她销毁,所以调用析构函数,但是先调用子类的析构函数,再调用父类的析构函数。