现在的位置: 首页 > 综合 > 正文

c++继承

2013年12月10日 ⁄ 综合 ⁄ 共 2496字 ⁄ 字号 评论关闭

第一次在csdn写博客~~

先看代码

.h

class ad1
{
public:
	ad1()
	{
		cerr<<"ad1 construction\n";
	}
	~ad1()
	{
		cerr<<"ad1 deconstruction\n";
	}
};

class ad2
{
public:
	ad2()
	{
		cerr<<"ad2 construction\n";
	}
	~ad2()
	{
		cerr<<"ad2 deconstruction\n";
	}
};
class test2;
class test1
{
	
public:
	test1(int a):tst_var(a){}
	test1()
	{
		cerr<<"test1 construction\n";
	}
	/*test1(test1 &t1)
	{
		cerr<<"test1 copy construction ,parameter is test1\n";
	}*/
	/*test1(test2 &t2)
	{
		cerr<<"test1 copy construction ,parameter is test2\n";
	}*/
	~test1()
	{
		cerr<<"test1 deconstruction\n";
	}
	
	
	void calc()
	{
		cerr<<"int test1\n";
	}
protected:
	int ptd1;
private:
	//A &a;
	int tst_var;
	static const int Snum ;
};

class test2:public test1
{
public:
	test2()
	{
		cerr<<"test2 construcion\n";
	}
	void calc()
	{
		cerr<<"int test2\n";
	}

	~test2()
	{
		cerr<<"test2 deconstruction \n";
	}

protected:
	ad1 a1;
	ad2 a2;

};

main.cpp

void f2(test1 ts)
{
	ts.calc();
}

main()
	test2 t2;
	cerr<<"befor f2\n";
	f2(t2);
	cerr<<"after f2\n";

output:

test1 construction
ad1 construction
ad2 construction
test2 construcion
befor f2
int test1
test1 deconstruction
test1 deconstruction
after f2
test2 deconstruction
ad2 deconstruction
ad1 deconstruction
test1 deconstruction

解释:

1. 继承类test2的构造过程是,先调用基类test1的构造函数,然后在分别待用test2成员变量的构造函数,最后才调用自己的构造函数,而析构的过程正好与构造的过程相反。

下面探讨派生类传递值的过程。

首先,test2被隐式转换为test1,生成一个匿名的test1的对象,然后,此匿名的对象,通过test1的默认的拷贝构造函数,在生成test1的对象ts。所以在函数调用过程中,产生了两个test1的临时对象,所以在析构的时候要析构两个;

如果我们添加test1的拷贝构造函数,

test1(test1 &t1)

t1是对test2的对象t2的一个引用,当然是不完全引用,即只能用t2中基类的内容,然后通过这个拷贝构造函数,生成f2的参数ts,所以只产生一个临时变量,析构时只会析构一次

2.关于多态的讨论
即使我们将f2改为void f2(test1 &ts)
此时不会有任何临时变量生成,但是ts也仅仅是对t2的不完全引用,也只能调用t2中基类的部分,所以还是调用基类的函数calc.以此类推,如果ts调用的不是重载函数calc,而是调用的test2的特有的函数,则编译不过,因为此时ts是不完全引用,就是只能当成基类用,所以这个时候我们就需要多态。
只需在test1的函数变为,virtual void calc(),此时再f2中调用的就是test2中的calc,但是依然不能调用test2中特有的函数,因为是不完全引用。

(也可以再基类中不设定virtual,在f2中强制降ts转为test2的对象,然后调用calc,这时调用的当然是test2的函数了,但是这样就没有能体现出多态的强大优势)
但是终究是派生类,所以可以在f2可以修改为
ts.calc();
test2 ts2 = static_cast<test2 &>(ts);
ts2.anotherFun();
将ts转换为test2
此时输出为:
test1 construction
ad1 construction
ad2 construction
test2 construcion
befor f2
int test2
test1 copy constructio
test2 anothre fun
test2 deconstruction
ad2 deconstruction
ad1 deconstruction
test1 deconstruction
after f2
test2 deconstruction
ad2 deconstruction
ad1 deconstruction
test1 deconstruction
还有提一点的是,对于
test2 ts2 = static_cast<test2 &>(ts);
调用的是test2的默认拷贝构造函数,而不是赋值函数
在test2的拷贝构造函数中,对其成员变量依次调用其拷贝构造函数
所以会调用到test1的拷贝构造函数
简单来记的话就是,如果对象在声明的同时将另一个已存在的对象赋给它,就会调用复制构造函数;如果对象已经存在,然后将另一个已存在的对象赋给它,调用的就是赋值运算符(重载)
当定义一个纯虚函数时,比如 virtual void calc() = 0;
则基类成为抽象类,抽象类不能实例化对象,但是可以声明一个抽象类的指针或者引用
3. 派生类的构造顺序
   如果将test2的构造函数改为test2():a2(),a1(),test1(6)
   则构造顺序还是不会变的,即先构造基类,再按照成员变量的声明顺序依次构造,即,test1,a1,a2
关于默认构造函数的更为详细的说明请看:

http://grantren.iteye.com/blog/43289





抱歉!评论已关闭.