拷贝构造,拷贝赋值,析构函数

在C++中,对于一个空的类,编译器一般会默认它有4个成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数。

class String
{
public:
  String(const char* cstr = 0);//构造函数
  String(const String& str);//拷贝构造函数
  String& operator= (const String& str);//拷贝赋值函数
  ~String();//析构函数
private:
  char* m_data;
};

   构造函数我们在以前的博客中介绍过,它的作用就是在创建一个新的对象时给数据成员赋初值,也就是给数据成员初始化。而析构函数会在对象跳出程序(死亡)前被自动调用来释放内存,否则会造成内存泄露(Memory leak)。析构函数没有返回值,也不能带任何参数。关于类的构造函数和析构函数的典型应用:在构造函数中用new来为指针成员开辟一个独立的动态内存空间,而在析构函数中用dalete来释放它。

inline
String::String(const char* cstr = 0)
{
  if(cstr){
    m_data = new char[strlen(cstr) + 1];
    strcpy(m_data, cstr);
  }
  else{ //未指定初值
    m_data = new char[1];
    *m_data = '\0';
  }
}
 
inline
String::~String()
{
  dalete[] m_data;
}
{
  String s1();
  String s2("hello");//调用构造函数创建对象
 
  String* p = new String("hello");//动态创建对象
  delete p;
}


    在C++中对一个对象的初始化可以是下面这样:

{
  String a("hello");//A:通过构造函数设定初值
  String b(a);//B:通过指定的对象设定初值
}


在上面的语句中,B语句将a作为b的初值,和a一样,b的初始化形式要调用相应的构造函数,但是此时找不到与之匹配的构造函数,因为String类中没有哪个构造函数的形参是String类的对象,由此引出String所隐含的一个特殊的默认构造函数,原型是:

String(const String&);


这种默认的构造函数就成为默认拷贝构造函数,不过这也会造成一些问题,如下图所示:

默认拷贝构造函数只是将b的指针指向a的内容,仅仅是将内存空间的内容(地址)做了拷贝,这种拷贝方式成为浅拷贝,且容易造成内存泄露。此时的解决办法就是自己定义一个拷贝构造函数,然后在进行数值拷贝之前为指针类型的数据成员重新开辟一个独立的内存空间,这种还需要另开辟新的内存空间的拷贝方式称作是深拷贝。

inline
String::String(const String& str)
{
  m_data = new char[strlen(str.m_data) + 1];//开辟新的内存空间
  strcpy(m_data, str.m_data);
}


    其实可以这么总结拷贝构造函数:拷贝构造函数的格式就是带有参数的构造函数。实际上,拷贝操作的实质就是类的对象的引用,所以在C++的规定中就说拷贝构造函数的参数个数可以是一个或者多个,但是第一个参数必须是类的引用对象。一旦在类中定义了拷贝构造函数,那么默认拷贝构造函数就不再有效。
    我们现在来考虑另一种赋值方法,现在要完成把b的值复制给a,第一步是清空a的值,第二步为a开辟b那么大的空间,第三步完成拷贝操作。这就是拷贝赋值函数的执行过程。

                            

                            

                            

inline
String& String::operator= (const String& str)
{
  if (this == &str)
    return *this; //检测自我赋值(self assignment)
  delete[] m_data; //Step1
  m_data = new char[strlen(str.m_data) + 1]; //Step2
  strcpy(m_data, str.m_data); //Step3
  return *this;
}
{
  String s1("hello");
  String s2(s1);
  s2 = s1; //调用拷贝赋值函数
}

                               
    总结:当类中有指针成员时,我们必须要自己定义拷贝构造和拷贝赋值函数。拷贝构造函数创建了新的对象实例,而拷贝赋值函数是将对象的值复制给一个已经存在的变量。

发布了115 篇原创文章 · 获赞 29 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/huabiaochen/article/details/104289166