【问题标题】:Copy constructor and dynamic memory复制构造函数和动态内存
【发布时间】:2017-06-25 10:25:01
【问题描述】:

我是编程初学者,我正在学习复制构造函数。从不同的来源我可以看到,如果我想“深度复制”一个类对象,那么复制构造函数很有用,这样新对象的指针成员将指向新的内存位置。

我的问题是,如果我使用空的复制构造函数(如在 EmptyCat 类中)得到相同的结果,那么像在我的示例中的类 CopyCat 中那样定义复制构造函数有什么好处?

我的第二个问题是,为什么 Cat 类和 EmptyCat 类的工作方式不同?它们之间的唯一区别是我在 EmptyCat 中定义了一个空的复制构造函数。但是当我运行程序时,我可以看到在 EmptyCat 中复制指针成员后指向一个新位置,而在 Cat 类中它作为浅拷贝工作。

#include "iostream"

class Cat
{
  public:
    void GetMem() { std::cout << itsAge << "\n"; }
  private:
    int * itsAge = new int;
};

class EmptyCat
{
  public:
    EmptyCat() {}
    ~EmptyCat() {}
    EmptyCat(EmptyCat&obj) {}
    void GetMem() { std::cout << itsAge << "\n"; }
  private:
    int * itsAge = new int;
};

class CopyCat
{
  public:
    CopyCat() {}
    ~CopyCat() {}
    CopyCat(CopyCat&obj);
    int GetAge() { return *itsAge; }
    void GetMem() { std::cout << itsAge << "\n"; }
  private:
    int * itsAge = new int;
};

CopyCat::CopyCat(CopyCat & obj)
{
    itsAge = new int;
    *itsAge = obj.GetAge();
}

int main()
{
    Cat Garfield;
    Cat Kitty(Garfield);

    std::cout << "Memory addresses for the objects' <itsAge> member:" << std::endl;
    std::cout << "Garfield and Kitty (Class Cat):" << std::endl;
    Garfield.GetMem();
    Kitty.GetMem();

    EmptyCat Meow;
    EmptyCat Purr(Meow);

    std::cout << std::endl << "Meow and Purr (Class EmptyCat):" << std::endl;
    Meow.GetMem();
    Purr.GetMem();

    CopyCat Fluffy;
    CopyCat Felix(Fluffy);

    std::cout << std::endl << "Fluffy and Felix (Class CopyCat):" << std::endl;
    Fluffy.GetMem();
    Felix.GetMem();

    system("pause");
    return 0;
}

如果我运行程序,我会得到:

Memory addresses for the objects' <itsAge> member:
Garfield and Kitty (Class Cat):
00BBDA60
00BBDA60

Meow and Purr (Class EmptyCat):
00BB46A0
00BB8280

Fluffy and Felix (Class CopyCat):
00BB82B0
00BBE8A0
Press any key to continue . . .

【问题讨论】:

  • 你的类泄漏了内存,所以它不是一个很好的例子。
  • 我想让它尽可能简单,重点关注复制构造函数的行为
  • 但是这样做你会错过所有有趣的东西。如果你的析构函数要删除分配,你会立即发现三规则等等。

标签: c++ copy-constructor deep-copy


【解决方案1】:

我的问题是,如果我使用空的复制构造函数(如在 EmptyCat 类中)得到相同的结果,那么像在我的示例中的类 CopyCat 中那样定义复制构造函数有什么好处?

您不会得到相同的结果。 CopyCat 分配新内存从旧类复制值。 EmptyCat 只是分配新内存,但不复制值。

我的第二个问题是,为什么 Cat 类和 EmptyCat 类的工作方式不同?它们之间的唯一区别是我在 EmptyCat 中定义了一个空的复制构造函数。但是当我运行程序时,我可以看到在 EmptyCat 中复制指针成员后指向一个新位置,而在 Cat 类中它作为浅拷贝工作。

Cat 中,您还没有声明复制构造函数,因此编译器会在需要时生成一个。默认的复制构造函数从原始的成员中复制。在您的情况下,这将复制指针(以便它存储与原始地址相同的地址)。

EmptyCat 中有一个用户定义的复制构造函数。但是由于那个不处理指针成员,所以将使用它的默认值。

int * itsAge = new int;

这就是分配一个新的int 并为您获取不同的指针值。

【讨论】:

  • 根据您的回答,我做了一些更改,并设法展示了空定义的复制构造函数的工作原理。具有空定义的复制构造函数实际上不是复制构造函数,它不复制任何内容,它实际上作为一个新对象创建工作。它基于类定义创建一个新对象,这就是我感到困惑的原因,因为我在类定义中有int * itsAge = new int; 语句(因此基于空的复制构造函数为新对象分配了新内存)。
  • 我做了一些进一步的研究,我发现所谓的隐式复制构造函数(在没有声明的复制构造函数时使用)相当于:Class::Class (const Class&amp; object): x(object.x), y(object.y), z(object.z) {}所以隐式复制构造函数不仅仅是一个空洞的定义。
【解决方案2】:

深拷贝和浅拷贝是一个 C 概念,其中只有结构和原始指针。可以拥有指针,在这种情况下副本必须是深的,或者可以共享,在这种情况下,副本是浅的(如果它是使用 malloc 分配的,则必须小心释放它)。

在 C++ 中,new 现在已被有效弃用。我们有唯一的指针,它们是“拥有指针”和“共享指针”。然而,指针相对较少。类的数组成员是 std::vectors,字符串成员是 std::strings。并且副本是自动深的,(如果您想要浅副本,请使用引用)。

指针在相对不寻常的情况下被保留,例如树和图表。

【讨论】:

    【解决方案3】:

    无论有没有空的复制构造函数,您都不会得到相同的行为。 EmptyCat(EmptyCat&amp; obj) { } 绝对什么都不做。

    CopyCat(CopyCat& obj) {
      itsAge = new int;
      *itsAge = obj.GetAge();
    }
    

    动态分配一个新的int 并为其分配来自obj 的值。

    【讨论】:

      猜你喜欢
      • 2011-03-11
      • 1970-01-01
      • 1970-01-01
      • 2018-09-10
      • 2021-06-17
      • 2013-10-13
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      相关资源
      最近更新 更多