编写类String的构造函数、拷贝构造函数、析构函数和赋值函数(附代码分析解读)

面试题:

用c++编写类的String的构造函数、拷贝构造函数、析构函数和赋值函数

具体实现代码如下:

#include<iostream>
using namespace std;
class String
{
private:
        char *p;
public:
        String(const char*p);   //普通构造函数
	String(const String& other);   //拷贝构造函数
	String& operator=(const String& other);  //赋值运算符重载函数
	~String(void);    //析构函数
	char* C_str(void) const;   //常量函数
};

String::String(const char*p)
{
	cout << "String::String(const char*p)" << endl;
	this->p = new char[strlen(p)+1];   //申请存放字符串的空间
	strcpy(this->p,p);   //用以传参
}
String::String(const String& other)
{
	cout << "String::String(const String& other)" << endl;
	this->p = new char[strlen(other.p)+1];  //自定义复制构造函数,避免指针的double free
	strcpy(this->p,other.p);
}
String& String::operator=(const String& other)
{
	cout << "operator=()is calling...." << endl;
	if(this != &other)
	{
		char *tmp=new char[strlen(other.p)+1];
		if( tmp==NULL )
			return *this;
		strcpy(tmp,other.p);
		delete[]p;
		p=tmp;
	}
	return *this;
}
String::~String(void)
{
	cout << "String::~String(void)" << endl;
	if(p)
	{
		delete[]p;
		p=NULL;
	}
}
char* String::C_str(void) const
{
	return p;
}
int main()
{
	String x("Hello,world");
	String y("my name is leikun");
	String z("comeon,baby!");
	cout << x.C_str() << endl;
	cout << y.C_str() << endl;
	cout << z.C_str() << endl;
	z=y=x;
	cout << z.C_str() << endl;
	String s1=x;
	cout << "s1=" << s1.C_str() << endl;
	return 0;
}

程序运行结果截图:


程序分析解读:

1:什么是赋值运算符函数?

默认的复制赋值运算符函数对于基本类型的成员变量,按字节复制,对于指针的类型成员变量值,复制指针本身,而不是指针所指向的真实数据,这和默认复制构造函数浅复制效果类似,当然,也会出现使用默认复制构造函数时的二次释放问题。所以,建议使用自定义复制赋值运算符函数;

赋值运算符函数的返回值本身没有限制,但建议将其返回值的类型定义为和内置该运算符函数的类型相同(eg: clock& operator=(const clock& t)  );

通常当类的数据成员存在指针类型时,就需要在构造函数中为该指针分配内存空间,在析构函数中释放空间,但是为了防止空间的重复释放,需要自定义拷贝构造函数和赋值运算符函数;

赋值运算符函数的实现流程通常分为五步,详情参见上面例子。(eg: String& operator=(const String& other)  )

2:拷贝构造函数和赋值函数之间区别与联系

两者都是从看起来有很大的相同点,都是从一个数据对象中赋值数据,但是使用时有很大的区别:具体归纳为以下三点:

是否产生新对象:

拷贝构造函数是一个构造函数,他调用时必须产生一个对象,也就是用一个已存在的对象去构造一个不存在的对象,重点体会“构造”两字;而复制赋值运算符函数是通过参数传进来,对已经存在的对象赋值,并不产生新对象,即用一个已经存在的对象去覆盖另一个已经存在的对象,重点体会“赋值”两字。

调用时机不同:

复制(拷贝)构造函数是用已经存在对象去初始化另一个新的对象,也就是说复制构造函数是在对象创建并初始化的时候调用;而复制赋值运算符函数要在对象初始化完成后调用。(eg:  Sting s("hello,world"); String t=s;  //此处调用复制构造函数;Sting s("hello,world"); String t; t=s;  //此处调用的是复制赋值运算符函数)。

是否释放原来的内存空间:

如果宿主类的数据成员是指针,都需要分配空间。复制构造函数需要重新申请内存空间,但不需要先释放原来的内存空间,这是因为不能和传递的对象公用相同的内存空间,否则在析构函数中会出现二次释放( double  free )的问题。而复制赋值运算符函数使用时,对象已经存在,也就是说数据指针的内存空间已经存在,但这段空间的长度不一定满足赋值的需求,所以需要先释放已有的内存空间,在根据赋值对象的成员大小重新申请内存空间,然后在进行赋值。

猜你喜欢

转载自blog.csdn.net/leikun153/article/details/80684075