我应该对构造函数中由 "new" 运算符初始化的对象成员使用 "delete" 吗?

Should I use the "delete" for the object member which initialized by "new" operator in constructor?

我有一个关于 newdelete 的问题: 输入参数或成员对象应该使用delete,例如:

https://github.com/jwbecalm/Head-First-Design-Patterns-in-CPP/blob/main/ch01_Strategy/main.cpp

我应该在 new FlyNewWay() 分配的对象上使用 delete 吗?

// change behavior in runtime
mallardDuck->setFlyBehavior(new FlyNewWay());

// create custom MallarDuck on stack.
MallarDuck rocketDuck =  MallarDuck(new FlyWthRocket(), new NewQuack());

在 MallardDuck 构造函数中:

https://github.com/jwbecalm/Head-First-Design-Patterns-in-CPP/blob/main/ch01_Strategy/MallarDuck.cpp

MallarDuck:: MallarDuck(){
    cout << "in MallarDuck:: MallarDuck()" << endl;
    // 为MallarDuck 赋值其特有的行为
    m_flyBehavior = new FlyWithWings();
    m_quackBehavior = new SimpleQuack();
}

我应该使用 delete 来释放 m_flyBehaviorm_quackBehavior 吗?但是这两个是对象的成员。

我可以轻松地在 MallarDuck 的析构函数中添加 delete

MallarDuck::~MallarDuck(){
    cout << "in ~MallarDuck()" << endl;
     delete m_flyBehavior;   
     delete m_quackBehavior;
}

但是第一个代码块中提到的输入参数呢?还是不推荐这种设计和传递价值的方式?你有什么建议吗?

setFlyBehavior()如下,如果我添加删除fb,它会删除m_flyBehavior 也是我的理解,因为fb和m_flyBehavior指向相同的内存地址。

void Duck::setFlyBehavior(FlyBehavior* fb){
    m_flyBehavior = fb;
    cout << "in Duck::setFlyBehavior()" <<endl;
}

在setFlyBehavior()之后,fb和m_flyBehavior都指向同一个内存地址。所以在解构器中:~MallarDuck(),如果我删除 m_flyBehavior,它指向的输入参数 fb 也将是免费的。 所以,deconstructor ~MallarDuck() 中的 delete 就足够了。谢谢.

是的,每当您使用 new 关键字分配一些内存时,您必须始终使用 delete 在以后不再需要时释放该内存。否则你会像在你的程序中那样发生内存泄漏。在您的情况下,您应该在 MallarDuck.cpp .

的析构函数中使用 delete

其他解决方案是使用像 unique_ptr 这样的智能指针,这样您就不必显式释放内存。

unique_ptr` 的语法类似于:

unique_ptr<FlyWithWings> m_flyBehavior(new FlyWithWings());

如果您对是否存在内存泄漏感到困惑,可以在FlyWithWingsSimpleQuack的析构函数中添加cout语句来确认是否存在内存泄漏是否在这些对象上调用析构函数。然后相应地你可以根据需要使用智能指针。

确认是否有内存泄漏的步骤

  1. FlyWithWingsSimpleQuack.
  2. 的析构函数中添加一些 std::cout<<"destructor ran; 语句
  3. 此外,在这种情况下,不要在 MallarDuck.
  4. 的析构函数内的数据成员上显式使用 delete
  5. 然后如果你看到析构函数不是运行数据成员m_flyBehaviorm_quackBehavior这意味着你有内存泄漏,你需要使用deleteMallarDuck.
  6. 的析构函数中显式地在适当的数据成员上

回答您的编辑

how about the input parameter mentioned in the first code block?

对于语句mallardDuck->setFlyBehavior(new FlyNewWay());你可以使用下面的代码:

void Duck::setFlyBehavior(FlyBehavior* fb){
    //delete the old memory but note again we should check whether the pointer is valid or not
   //check for a valid pointer
    if (m_flybehavior)
    {
       
        delete m_flybehavior;//free the old memory
    }
    
    m_flyBehavior = fb;//so m_flybehavior points to some new memory

    //no need to use delete now on m_flybehavior because  in the destructor ~MallarDuck() you have delete m_flybehavior
    cout << "in Duck::setFlyBehavior()" <<endl;
}

而对于声明

MallarDuck rocketDuck =  MallarDuck(new FlyWthRocket(), new NewQuack());

因为 MallarDuck 的析构函数有 delete 作为数据成员,它将释放内存。这也假定输入参数已分配给数据成员。

所有使用 new 创建的指针必须在某个时候删除。在 C++ 中,您通常不会像 setFlyBehavior() 那样提供指向函数的指针,因为谁应该删除指针并不明显。但是,您似乎正在使用多态性,因此您需要使用某种指针。

如果像setFlyBehavior(new FlyNewWay())那样调用函数,class应该删除指针。但是用户也可以使用现有指针调用它并在以后删除它。指针会被多次删除。

// Incorrect usage
FlyBehavior* way = new FlyNewWay();
duck1->setFlyBehavior(way);
duck2->setFlyBehavior(way);
delete way;

您的 class 无法知道用户是如何创建指针的。您不应该相信 class 被正确使用。相反,将 std::unique_ptr<FlyBehavior> 作为参数并将其保存在 class.

创建参数 std::make_unique:

mallardDuck->setFlyBehavior(std::make_unique<FlyNewWay>());