C++ Insight: 拷贝构造函数、赋值运算符、指针、引用

@ 2009-08-08 16:33:35
标签:

    拷贝和赋值

    拷贝和赋值都是为了使一个变量具有和某个变量一样的值。C++ 中的拷贝是指 拷贝构造函数 (Copy Constructor),赋值是指重载运算符 = 。
    它们看起来可能如下。

    class A {
    public:
      int _v;
    public:
      A(const A &a){ // 拷贝构造函数
        _v = a._v; 
      } 
      A& operator = (const A &a) {   // 赋值运算符
        _v = a._v; 
        return *this; 
      } 
    };
    

    注意上面赋值运算符中的 "return *this"。如果去掉这一句会有什么影响呢?

    既然都是使为了复制变量的值,又为什么又需要两种操作呢。考虑下面的代码:

    classA a;
    
    classA b(a);   // 调用 copy constructor
    
    classA c; // 调用default constructor
    c = a; // 调用 assignment operator
    

    拷贝构造函数和赋值运算符可以说是事物的一体两面。

    拷贝和赋值都是对一个对象进行复制,是需要耗费资源的。为了节省资源,就有了引用的概念。

    指针和引用

    先看如下代码:

    classA a;
    a._v = 3;
    
    classA b = a;
    
    classA c;
    c = a;
    a._v = 5;
    std::cout<< c._v << std::endl; // 输出 3
    
    classA &d = a; // 引用 
    a._v = 6;
    std::cout<< d._v << std::endl; // 输出 6
    
    classA e;
    &e = a;  // 错误用法
    
    class A *f;
    f = &a; // 指针
    std::cout<< f->_v << std::endl; // 输出 6
    

    前两种情况 (b=a, c=a)我们已经讲过,效果上是一样的,只是用了不同的方式实现,其中 c=a 的实现更费资源,因为多了一步构造调用默认函数。

    d = a 和 b=a 相比多了一个 '&'号,表示引用,这样 d 和 a 实际上指向内存中同一个地址。这个就叫做引用。看似两个不同的变量,实则是同一个对象。

    至于 e = a,则是错误的用法。因为这里的 '&' 号成立取地址符号。等号左边是地址,右边是对象,不是同一个概念。改成下面的 f = &a 就对了。这个就是指针。指针的实质就是对象的地址。其中的'&'号表示获取对象 a 的地址。通过指针访问对象就要用符号 '->' 了,这与变量用'.'是不一样的。

    明白了上面的例子,应该就抓住了指针和引用的本质。

    实例分析

    下面的例子既是对拷贝构造函数的分析,也是对值或值的引用应用的分析,最好能深刻理解。

    #include &lt;iostream&gt;
    
    class A {
    public:
      int v;
      A(int i):v(i){
        std::cout&lt;&lt;"init A ..."&lt;&lt;v&lt;&lt;std::endl;
      }
      A(const A &a){
        v = a.v;
        std::cout&lt;&lt;"copy A ..."&lt;&lt;v&lt;&lt;std::endl;
      }
    
      ~A(){
        std::cout << "destroy A ..." << v<< std::endl;
      }
    
      void print(){
        std::cout<<"print A ..."<< v<< std::endl;
      }
    };
    
    class B {
    public:
      int v;
      A   class_a;
    
      B(int i, int j):v(i),class_a(j){
        std::cout<<"init B ..."<< v<< std::endl;
      }
      ~B(){
        std::cout<<"destroy B ..."<< v<< std::endl;
      }
    
      void print(){
        std::cout<<"print B ..."<< v<< std::endl;
      }
      A& getA1(){
          return class_a;
      }
    
      A  getA2(){
          return class_a;
      }
    };
    
    
    int main(int argc, char **argv){
      B b(3,4);
      std::cout<<"----------"<< std::endl;
      b.getA1().print(); 
      std::cout<<"----------"<< std::endl;
      b.getA2().print(); 
      std::cout<<"----------"<< std::endl;
      {
        A a1  = b.getA1();
        a1.v = 5;
      }
      std::cout<<"----------"<< std::endl;
      {
        A a1  = b.getA2();
        a1.v = 6;
      }
      std::cout<<"----------"<< std::endl;
      {
        A &a2 =b.getA1();
        a2.v = 7;
      }
      std::cout<<"----------"<< std::endl;
      {
        // A &a2 =b.getA2(); // error this one 
        // a2.v = 8;
      }
      std::cout<<"----------"<< std::endl;
      return 0;
    }
    
    
    

    上面程序的输出结果如下:

    init A ...4
    init B ...3
    ----------
    print A ...4
    ----------
    copy A ...4
    print A ...4
    destroy A ...4
    ----------
    copy A ...4
    destroy A ...5
    ----------
    copy A ...4
    destroy A ...6
    ----------
    ----------
    destroy B ...3
    destroy A ...7
    

    TODO: 拷贝构造函数和赋值运算时的自赋值

    网络资源

    标签:

      分享到:
      comments powered by Disqus

      46/48ms