如何安全地使用基类指针进行子类的深拷贝

当初刚从C++转到Java时,觉得接口概念很神奇并且貌似没有什么用处。然而用惯了它以后,再回到C++,却时常因为C++的虚函数/继承机制没有Java接口用着方便而烦心。

今天编一个仿真程序就碰上了问题:某个任务X有好几种算法算法A算法B并且都有可能用到,于是写了个带虚函数的基类,然后用两种方法去继承,代码大概如下:

test1.cpp
#include <iostream>
 
class A
{
public:
	virtual void fuck()=0;
};
 
class B : public A
{
public:
	B(int t)
			: t(t) {}
 
	B(const B &src)
	{
		t = src.t;
	}
 
	int t;
 
	void fuck() override
	{
		std::cout << "Fuck B " << t << " times." << std::endl;
	}
 
 
};
 
class C : public A
{
public:
	C(int t)
			: t(t) {}
 
	C(const C &src)
	{
		t = src.t;
	}
 
	int t;
 
	void fuck() override
	{
		std::cout << "Fuck C " << t << " times." << std::endl;
	}
};
 
int main()
{
	A *ab = new B(233);
	ab->fuck();
 
	A *ac = new C(666);
	ac->fuck();
 
	return 0;
}

输出:

Fuck B 233 times.
Fuck C 666 times.

同样的A*指针给出了不同的结果,操作是成功的。然而下一步需求就复杂了:由于某些原因,需要从一个A*做深拷贝。

显然,不能直接实例化一个A然后逐个拷贝元素,因为A不能直接被实例化。然而,需要做拷贝的地方是不知道这个指针究竟指向的是B还是C的(如果这也要手动处理,我还不如去写一堆if呢)。

那么按虚函数机制,写一个虚的virtual A(const A &a)=0;呢?也不行,构造函数就是为了实例化对象,不能为虚。至此为止,貌似山穷水尽。

思考一下,还是要从虚函数机制下手,必须通过虚函数机制,才能从一个A*调用到B或者C里面去。构造函数不让玩虚的,就自己来一个普通的函数代替嘛。

按这个思路,一个virtual A *copy()=0;就可以解决问题了,经验证,除了编码稍微麻烦了些之外,没什么隐患,代码与输出如下:

test2.cpp
#include <iostream>
 
class A
{
public:
 
	virtual A *copy()=0;
 
	virtual void fuck()=0;
 
	virtual ~A()
	{
		std::cout << "Stop fucking around" << std::endl;
	}
};
 
class B : public A
{
public:
	B(int t)
			: t(t) {}
 
	B(const B &src)
	{
		t = src.t;
	}
 
	int t;
 
	A *copy() override
	{
		std::cout << "I am B and I am making a copy of A" << std::endl;
		return new B(*this);
	}
 
	void fuck() override
	{
		std::cout << "Fuck B " << t << " times." << std::endl;
	}
 
	~B() override
	{
		std::cout << "Stop fucking B around" << std::endl;
	}
};
 
class C : public A
{
public:
	C(int t)
			: t(t) {}
 
	C(const C &src)
	{
		t = src.t;
	}
 
	int t;
 
	A *copy() override
	{
		std::cout << "I am C and I am making a copy of A" << std::endl;
		return new C(*this);
	}
 
	void fuck() override
	{
		std::cout << "Fuck C " << t << " times." << std::endl;
	}
 
	~C() override
	{
		std::cout << "Stop fucking C around" << std::endl;
	}
};
 
int main()
{
	A *ab = new B(233);
	ab->fuck();
	A *ab_copy = ab->copy();
	ab_copy->fuck();
 
	A *ac = new C(666);
	ac->fuck();
	A *ac_copy = ac->copy();
	ac_copy->fuck();
 
	std::cout << "Address: ab: " << ab << "\tab_copy: " << ab_copy << "\tac: " << ac << "\tac_copy:" << ac_copy
			  << std::endl;
 
	delete ab;
	delete ab_copy;
	delete ac;
	delete ac_copy;
 
	return 0;
}

输出:

Fuck B 233 times.
I am B and I am making a copy of A
Fuck B 233 times.
Fuck C 666 times.
I am C and I am making a copy of A
Fuck C 666 times.
Address: ab: 0x13f0c20	ab_copy: 0x13f1050	ac: 0x13f1070	ac_copy:0x13f1090
Stop fucking B around
Stop fucking around
Stop fucking B around
Stop fucking around
Stop fucking C around
Stop fucking around
Stop fucking C around
Stop fucking around
  • 最后更改: 2019/05/24 01:09