2011-02-17 252 views
8

我的理解是,当你制作一个定义指针变量的类的拷贝时,拷贝指针,但是指针指向的数据不是。C++类拷贝(指针拷贝)

我的问题是:是否假设在这种情况下的“指针副本”只是实例化一个新的指针(动态内存分配)相同类型?例如,新指针只是一个包含任意内存地址的新分配,并且应该注意将该新指针指向适当的内存地址?

我认为对这个问题有一个相当简单的答案,我为它的微不足道的本性表示歉意,但我试图在更深层次上理解指针,并且这出现在我在互联网上的研究指针上。

问候,

乍得

+0

指针和它们指向的数据是分开的。马丁的答案应该澄清如果不检查基本知识:http://www.cplusplus.com/doc/tutorial/pointers/ – AJG85 2011-02-17 19:01:00

回答

13

的指针将被简单地复制作为一种价值 - 所以这两个类将指向相同的原始记忆,没有新的分配发生。 浅拷贝 - 这是默认情况下的语言。

如果您需要分配新内存并复制数据,您必须自己在复制构造函数中执行该操作。 深拷贝 - 你必须自己做这个

编辑:这是C++的优点之一,你可以自由决定如何复制工作。它可能是只有读取访问内存的对象的副本可以避免复制内存的成本。如果新对象需要写入,您也可以实现只复制原始数据的类。

+1

@Chad Kemp:另请参阅`boost :: shared_ptr`和*智能指针*。还研究*浅拷贝*和*深拷贝*之间的区别。 – 2011-02-17 19:12:17

1

复制的指针将指向完全相同的地址。没有新的分配。

0

是的,指针只是包含内存地址,如果你想做一个更深的副本,你需要自己编写代码,在复制构造函数中。

如果您始终通过同一类的指针引用同一类型的数据,并且需要将数据与对象一起复制,那么您也可以考虑将它只作为普通成员,而不是指针。

5

首先,类中的指针是静态的(即编译器知道这个类中有一个指针,它的大小是多少,所以当类被实例化时不需要动态内存分配)。

如果您复制类(并且没有定义特殊的复制构造函数),那么新类中的指针将指向与旧类中指针相同的内存位置。为了澄清:

#include <iostream> 

class A { 
    public: 
     int *p; 
}; 

int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 3 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

现在如果你想在复印时,你需要写一个拷贝构造函数和拷贝赋值构造函数来分配新的内存:

#include <iostream> 

class A { 
    public: 
     int *p; 

     A() : p(0) {} 
     A(const A& other) { // copy constructor 
      p = new int(*other.p); 
     } 

     A& operator=(const A& other) { // copy assignment constructor 
      // protect against self assignment 
      if (this != &other) { 
       if (p != 0) { 
        *p = *other.p; 
       } else { // p is null - no memory allocated yet 
        p = new int(*other.p); 
       } 
      } 
      return *this; 
     } 

     ~A() { // destructor 
      delete p; 
     } 
}; 


int main() { 
    A a,b; 
    a.p = new int(10); 
    b = a; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 10 

    *(b.p) = 3; 

    std::cout << "*(a.p) = " << *(a.p) << std::endl; // 10 
    std::cout << "*(b.p) = " << *(b.p) << std::endl; // 3 

    return 0; 
} 

当你这样做,你也应该写一个析构函数(见Rule of three),因为如果类被破坏,在复制/复制分配构造函数中分配的内存需要取消分配:

2

指针不会实例化动态内存分配。指针和分配是完全不同的东西。

如果您复制指向动态分配的内存的指针,则会有两个指针指向相同的分配内存。既然你已经复制它,它已经指向了内存块。具体来说,如果使用编译器生成的拷贝构造函数,新指针将指向与旧指针完全相同的内容。如果没关系,你不需要做任何事情。

您确实存在何时释放内存的问题。释放它两次通常会导致堆腐败,这是讨厌的。不释放它会导致内存泄漏,在某些情况下这可能是可以接受的。在另一个指针通过之前释放它也会导致问题。出于这个原因,有多个指向相同内存的人经常进入Boost项目,并使用他们的shared_ptr模板(这将在即将出现的新标准中出现,并且存在于大多数最新的系统中)。

如果您希望每个指针指向单独的内存块,您必须通过编写自己的拷贝构造函数来设置它,并分配一个新块并在其中复制必要的数据。 (出于完全相同的原因,您还需要编写自己的赋值运算符,以及您自己的析构函数,以便释放内存。有一条经验法则,称为三条法则,即如果您需要编写自己的副本构造函数,赋值运算符或析构函数,您可能需要编写它们全部)。