C++子类对象—子类对象的构造和析构
对象在使用之前,始终是要经历“构造”这个过程的。在第15章,我们了解到当一个对象的成员数据是另一个对象的时候,就先运行成员对象的构造函数,再运行父对象的构造函数。但是继承的出现,会引入子类的构造函数。这时候,这些构造函数的运行顺序又是怎样的呢?
//node.h和linklist.h同程序17.3.2
//stack.h
#include "linklist.h"
class Stack:private Linklist//私有继承链表类
{
public:
bool push(int i,char c);
bool pop(int &i,char &c);
void show();
Stack(int i,char c);
Stack();
};
Stack::Stack(int i,char c):Linklist(i,c)//将子类构造函数的参数传递给父类的构造函数
{
cout <<"Stack constructor with parameter is running..." <<endl;
}
Stack::Stack()//子类构造函数
{
cout <<"Stack constructor is running..." <<endl;
}
bool Stack::push(int i,char c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
return Insert(i,c);
}
bool Stack::pop(int &i,char &c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
i=pcurrent->idata;
c=pcurrent->cdata;
return Delete();
}
void Stack::show()
{
Show();
}
//main.cpp
#include <iostream>
#include "stack.h"
int main()
{
Stack ss(1,'4');//调用带参数的构造函数
cout <<"Stack ss constructed" <<endl;
ss.show();
Stack zz; //调用不带参数的构造函数
cout <<"Stack zz constructed" <<endl;
zz.show();
return 0;
}
运行结果:
Node constructor is running...
Linklist constructor is running...
Stack constructor with parameter is running...
Stack ss constructed
1 4
Node constructor is running...
Linklist constructor is running...
Stack constructor is running...
Stack zz constructed
0 0
Linklist destructor is running...
Node destructor is running...
Linklist destructor is running...
Node destructor is running...
这个程序中有三个类,其中Stack类是Linklist类的子类,Node类的对象是Linklist类的成员数据。根据程序的运行结果,我们可以确定,父类的成员对象仍然是最先构造的,接着是运行父类的构造函数,最后运行子类的构造函数。也就是说子类对象是在父类对象的基础上扩展而成的。
另外,如果我们希望把子类的构造函数的参数传递给父类的构造函数时,可以在子类的构造函数定义中用以下格式调用父类的构造函数:
子类名::构造函数名(参数表):父类名(参数表)
如程序17.4.1就是用上述方法实现子类和父类的构造函数参数传递。这样的方法不仅使子类对象的初始化变得简单,并且使子类和父类的构造函数分工明确,易于维护。
//node.h和linklist.h同程序17.3.2
//stack.h
#include "linklist.h"
class Stack:private Linklist
{
public:
bool push(int i,char c);
bool pop(int &i,char &c);
void show();
Stack(int i,char c);
Stack();
~Stack();//析构函数
};
Stack::Stack(int i,char c):Linklist(i,c)
{
cout <<"Stack constructor with parameter is running..." <<endl;
}
Stack::Stack()
{
cout <<"Stack constructor is running..." <<endl;
}
Stack::~Stack()
{
cout <<"Stack destructor is running..." <<endl;
}
bool Stack::push(int i,char c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
return Insert(i,c);
}
bool Stack::pop(int &i,char &c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
i=pcurrent->idata;
c=pcurrent->cdata;
return Delete();
}
void Stack::show()
{
Show();
}
//main.cpp
#include <iostream>
#include "stack.h"
int main()
{
Stack zz;
cout <<"Stack zz constructed" <<endl;
zz.show();
return 0;
}
运行结果:
Node constructor is running...
Linklist constructor is running...
Stack constructor is running...
Stack zz constructed
0 0
Stack destructor is running...
Linklist destructor is running...
Node destructor is running...
根据运行结果,我们可以确认:使用了继承之后,析构函数的运行顺序依然恰好与构造函数的运行顺序相反。
子类对象的构造
讨论子类对象的构造,就是在讨论子类对象的生成方式。它是先生成父类对象的成员,再对其进行扩展呢,还是先生成子类对象的成员,然后再对其进行补充?我们还是修改一下程序17.3.2,用事实来解决这个问题:(程序17.4.1)//node.h和linklist.h同程序17.3.2
//stack.h
#include "linklist.h"
class Stack:private Linklist//私有继承链表类
{
public:
bool push(int i,char c);
bool pop(int &i,char &c);
void show();
Stack(int i,char c);
Stack();
};
Stack::Stack(int i,char c):Linklist(i,c)//将子类构造函数的参数传递给父类的构造函数
{
cout <<"Stack constructor with parameter is running..." <<endl;
}
Stack::Stack()//子类构造函数
{
cout <<"Stack constructor is running..." <<endl;
}
bool Stack::push(int i,char c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
return Insert(i,c);
}
bool Stack::pop(int &i,char &c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
i=pcurrent->idata;
c=pcurrent->cdata;
return Delete();
}
void Stack::show()
{
Show();
}
//main.cpp
#include <iostream>
#include "stack.h"
int main()
{
Stack ss(1,'4');//调用带参数的构造函数
cout <<"Stack ss constructed" <<endl;
ss.show();
Stack zz; //调用不带参数的构造函数
cout <<"Stack zz constructed" <<endl;
zz.show();
return 0;
}
运行结果:
Node constructor is running...
Linklist constructor is running...
Stack constructor with parameter is running...
Stack ss constructed
1 4
Node constructor is running...
Linklist constructor is running...
Stack constructor is running...
Stack zz constructed
0 0
Linklist destructor is running...
Node destructor is running...
Linklist destructor is running...
Node destructor is running...
这个程序中有三个类,其中Stack类是Linklist类的子类,Node类的对象是Linklist类的成员数据。根据程序的运行结果,我们可以确定,父类的成员对象仍然是最先构造的,接着是运行父类的构造函数,最后运行子类的构造函数。也就是说子类对象是在父类对象的基础上扩展而成的。
另外,如果我们希望把子类的构造函数的参数传递给父类的构造函数时,可以在子类的构造函数定义中用以下格式调用父类的构造函数:
子类名::构造函数名(参数表):父类名(参数表)
如程序17.4.1就是用上述方法实现子类和父类的构造函数参数传递。这样的方法不仅使子类对象的初始化变得简单,并且使子类和父类的构造函数分工明确,易于维护。
子类对象的析构
在第15章中介绍析构函数的时候,我们就说它的运行顺序往往是和构造函数的运行顺序相反的。那么使用了继承之后,是否依然是这样的规律呢?我们继续修改程序17.4.1,尝试验证我们的猜想。//node.h和linklist.h同程序17.3.2
//stack.h
#include "linklist.h"
class Stack:private Linklist
{
public:
bool push(int i,char c);
bool pop(int &i,char &c);
void show();
Stack(int i,char c);
Stack();
~Stack();//析构函数
};
Stack::Stack(int i,char c):Linklist(i,c)
{
cout <<"Stack constructor with parameter is running..." <<endl;
}
Stack::Stack()
{
cout <<"Stack constructor is running..." <<endl;
}
Stack::~Stack()
{
cout <<"Stack destructor is running..." <<endl;
}
bool Stack::push(int i,char c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
return Insert(i,c);
}
bool Stack::pop(int &i,char &c)
{
while (pcurrent->next!=NULL)
pcurrent=pcurrent->next;
i=pcurrent->idata;
c=pcurrent->cdata;
return Delete();
}
void Stack::show()
{
Show();
}
//main.cpp
#include <iostream>
#include "stack.h"
int main()
{
Stack zz;
cout <<"Stack zz constructed" <<endl;
zz.show();
return 0;
}
运行结果:
Node constructor is running...
Linklist constructor is running...
Stack constructor is running...
Stack zz constructed
0 0
Stack destructor is running...
Linklist destructor is running...
Node destructor is running...
根据运行结果,我们可以确认:使用了继承之后,析构函数的运行顺序依然恰好与构造函数的运行顺序相反。