构造与析构方案

c++为初始化提供了三种方案:

1、无参构造函数——完成对象的初始化,当写了构造函数,调用写的构造函数;如果没写,调用默认构造函数。

2、带参构造函数(调用方法有三种)

3、复制构造函数(拷贝构造函数)(调用方法有4种)

 

带参构造函数的用法:

teacher techr("wangling" ,12 ,0001);
class _test
{
    public:
        test(int ad)
        {
              a = ad;
         } 
    private:
        int a;
};

test t2 = 11;   //调用有参构造函数
test t3 = (test)(12);  //程序员手动调用带参构造函数

拷贝构造函数的四种应用场景:

//语法:
类名::类名(const 类名& 对象名)
A a1;
A a2= a1;  //场景1,区别 A a2; a2=a1;赋值操作符重载调用
A a1;
A a2(a1);  //场景2
void fun(A a)
{

}

A a1;
fun(a1);    //传递参数时,调用拷贝构造函数,相当于 A a = a1; 怎么理解,因为形参a是要被分配空间的,要被构造,所以调用构造函数,而不是赋值重载调用
A fun()
{
     A a;
     return a;
}

void main()
{
     A a1;
     a1 = fun();    //先调用拷贝构造函数,再析构a,再调用赋值重载函数,再析构匿名空间
A a2 = fun(); //调用拷贝构造函数,再析构a,不会调用赋值重载函数,不析构匿名空间 } /* 为什么要调用拷贝构造函数? 因为,在完成函数调用时,函数要求返回一个对象,那么会首先创建一个临时的匿名对象空间,并把 return的 a拷贝给匿名的对象。所以调用了拷贝构造函数。
在执行完 a1 = fun();之后,匿名空间被释放,自动调用析构函数

对于 a2,在调用fun函数时,生成的匿名对象,被取名为a2,这样处理,可以提高效率,没有必要再构造一个新的对象了,因为匿名对象的存在,不用白不用。
所以调用完fun函数生成的匿名对象,也不马上析构它。当a2释放时才析构。不要认为匿名对象到a2又有一次拷贝构造函数调用。
*/

 

默认拷贝构造函数,也叫做浅拷贝构造函数,在未定义拷贝构造函数时,在用到上述场景的初始化时,调用默认拷贝构造函数,如果定义了拷贝构造函数,那么调用自己定义的。浅拷贝的特点:只是对象变量的简单复制。

深拷贝构造函数,一般是自己动手写的拷贝构造函数。

拷贝特点:拷贝对象成员变量和内存空间 

class name
{
      public:
           name(const char* p_name)
           {
                    pname=(char*)malloc(sizeof(p_name)+1);
                    strcpy(pname, p_name);
            }
           
            name(const name &obj)
            {
                   pname=(char*)malloc(sizeof(obj.pname)+1);
                   strcpy(pname , obj.pname);
             }

            ~name()
            {
                    free(pname);
pname = NULL; }
private: char* pname; }

 

 对象赋值操作:

name obj1;
name obj2;
obj2 = obj1;

默认赋值操作与浅拷贝,可能导致对象析构时出错,原因是:默认赋值操作函数也默认的拷贝操作都是成员变量的简单赋值,只要类中有指针域,那么可能有两个对象的指针指向同一块内存空间,由于不能同时释放两次,所以可能出现错误。怎么解决这个问题——赋值重载。

name operator=(const name obj)
{
       pname=(char*)malloc(sizeof(obj.pname)+1);
       strcpy(pname , obj.pname);
       return *this;
}

这样存在一个问题:例如

void main()
{
     name obj1("helloworld");
     name obj2("hahahahah");
     
    obj2 = obj1;
}

因为在构建obj2时创建了一个对象,它指向一段内存空间存放的是:“”hahahah" ,但是在执行赋值操作时,又malloc了一个新的空间,然后用obj2的pname执行了这个新的空间,并往这个新空间赋值“helloworld”,那么之前的"hahahahah"空间没有释放的机会——导致内存泄漏。怎么解决这个问题——:

name operator=(const name obj)
{
       if(pname != NULL)
       {
              free(pname);    //释放之前的空间
              pname = NULL;
       }    
   
       pname=(char*)malloc(sizeof(obj.pname)+1);
       strcpy(pname , obj.pname);
       return *this;
}

 

构造函数规则:

当类中没有定义构造函数时,编译器会生成默认构造函数和拷贝构造函数。

当类中定义了构造函数(无参或带参或拷贝),那么编译器不会生成默认构造函数(无参)。

默认的拷贝构造函数是浅拷贝构造函数。

 

构造函数的初始化列表:

必须用初始化列表初始化的情况:

1、const成员

2、引用类型成员

3、对象

初始化列表的初始化顺序与构造函数调用顺序是无关的,构造函数的调用遵行:基类构造函数-->成员对象构造函数(先定义先构造)--->派生类构造函数

为什么要引入初始化列表来初始化这些成员变量?因为普通初始化方式的构造函数不能进行它们的初始化。

class A
{
public:
    A(int a1)
    {
          mya=a1;
    }
private:
    int mya;
}

class B
{
public:
     B(int x ,int y):mya(x)
     {
          myb = y;
     }
private:
     A mya;
     int myb;     
}

 

匿名对象的生命周期:

class A
{
      public:
           A(int a)
           {
                mya = a;
           }
           ~A()
           {

            }
      private:
           int mya;
}

void main()
{
      A(3);    //调用构造函数,生成匿名对象,然后析构
      
      A a(4);  //调用构造函数,生成匿名对象取名为a,不马上析构
}

构造函数中能不能调用构造函数?

class A
{
      public:
           A(int a,int b,int c)
           {
                mya = a;
                myb = b;
                myc = c;
           }

          A(int a,int b)
           {
                mya = a;
                myb = b;
     
                A(a,b,100);   //怎么执行
           }

           ~A()
           {

           }
      private:
           int mya;
}

编译是能够通过的,正如前面所说,直接调用构造函数时,是会生成一个匿名的对象的,A(a,b,100); 生成的匿名对象的确能够将c赋值为100,但是执行完这个语句之后,马上析构掉匿名对象,所以c的空间又被释放掉了。所以最中c的值还是垃圾值。

 

posted @ 2016-10-17 14:08  e-data  阅读(110)  评论(0)    收藏  举报