在构造函数中使用new时应注意的事项

在构造函数中使用new时应注意的事项

使用new初始化对象的指针成员时必须特别小心。
● 如果在构造函数中使用new来初始化指针成员,则应该在析构函数中使用delete。
● new和delete必须相互兼容。new对应于delete,new[]对应于delete[]。
● 如果有多个析构函数,则必须以相同的方式使用new,要么都带中括号,要么都不带。因为只有一个析构函数,所有的构造函数都必须与它兼容。然而,可以在一个构造函数中使用new初始化指针,而在另一个构造函数中将指针初始化为空,这是因为delete(无论是带中括号还是不带中括号)可以用于空指针。
● 应定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象。

String::String(const String &st)
{
    
    
	num_strings++; //如果需要,则处理静态成员更新
	len = st.len; //复制字符串相同长度
	str = new char[len + 1]; //分配空间
	strcpy(str, st.str); //复制字符串给新的地址
}

复制构造函数应分配足够的空间来存储复制的数据,并复制数据,而不仅仅是数据的地址,还应该更新所有受影响的静态类成员。
● 应当定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象。

String & String::operator=(const String &st)
{
    
    
	if (this == &st) //分配对象给它本身
		return *this;
	delete[] str; //释放以前的字符串
	len = st.len; 
	str = new char[len + 1]; //获得空间给新的字符串
	strcpy(str, st.str); //复制字符串
	return *this; //返回调用对象的引用
}

检查自我赋值的情况,释放成员指针以前指向的内存,复制数据而不仅仅是数据的地址,并返回一个指向调用对象的引用。

应该和不应该

下面的摘要包含了两个不正确的示例,和一个良好的构造函数示例:

String::String()
{
    
    
   str = "default string"; //没有new[]
   len = std::strlen(str);
}

String::String(const char* s)
{
    
    
   len = std::strlen(s);
   str = new char; //没有[]
   std::strcpy(str, s); //没有申请空间
}

String::String(const String &st)
{
    
    
   len = st.len;
   str = new char[len+1]; //分片空间
   std::strcpy(str, st.str); //拷贝值
}

第一个构造函数没有使用new来初始化str。对默认对象调用析构函数时,析构函数使用delete来释放str。对不是使用new初始化的指针使用delete时,结果将是不确定的,并可能有害的。可将该构造函数修改为下面的任何一种形式:

String::String()
{
    
    
   len = 0;
   str = new char[1]; //利用new和[]
   str[0] = '\0';
}

String::String()
{
    
    
   len = 0;
   str = 0; //C++11 str=nullptr
}

String::String()
{
    
    
   static const char* s = "C++"; //初始化仅一次
   len = std::strlen(s);
   str = new char[len+1]; //利用new和[]
   std::strcpy(str, s);
}

第二个构造函数使用了new,但分配的内存量不正确。因此,new返回的内存块只能保存一个字符。试图将过长的字符串复制到该内存单元中,将导致内存问题。另外,这里使用的new不带中括号,这与另一个构造函数的正确格式不一致。

第三个构造函数正确。

最后,下面的析构函数无法与前面的构造函数正常地协同工作:

String::~String()
{
    
     
   delete str; //应该delete[]str;
}

该析构函数未能正确使用delete。由于构造函数创建的是一个字符数组,因此析构函数应该删除一个数组。

包含类成员的类的逐成员复制

假设类成员的类型为String类或标准string类:

class Magazine
{
    
    
private:
    String title;
    string publisher;
...
};

String和string都使用动态内存分配,但这不意味着需要为Magazine类编写复制构造函数和赋值运算符。

扫描二维码关注公众号,回复: 13226385 查看本文章

默认的逐成员复制和赋值行为有一定的智能。如果将一个Magazine对象复制或赋值给另一个Magazine对象,逐成员复制将使用成员类型定义的复制构造函数和赋值运算符。

复制成员title时,将使用String的复制构造函数,而将成员title赋给另一个Magazine对象时,将使用String的赋值运算符,依次类推。然后,如果Magazine类因其他成员需要定义复制构造函数和赋值运算符,情况将更复杂:这种情况,这些函数必须显式调用String和string的复制构造函数和赋值运算符。

猜你喜欢

转载自blog.csdn.net/qq_36314864/article/details/119675423