使用delete释放内存与智能指针的区别,以及正确释放内存的方式

4
我正在为大学做一个项目,想找出如何正确删除内存,以及我想出的删除方式是否与使用智能指针具有相同效果。
这是一个类,它将保存公司所有员工和团队,基本上具有指向某些员工的指针向量。
class Company
{
private:
    std::string companyInfo;
    std::vector<Employee * > employees;
    std::vector<Team *> teams;
public:
    Company();
    ~Company();

    std::string getCompanyInfo() const;
    void setCompanyInfo(const std::string & companyInfo);
    bool addEmployee(Employee * employee, const std::string & teamName);
    bool addTeam(Team * team);
    void printTeams() const;
    void printEmployees() const;
};

我的问题是:这种释放内存的方法是否正确,如果正确,它是否能够提供与使用智能指针自动化过程相同的结果。
Company::~Company()
{
    for (Employee * e : employees) {
        delete e;
    }

    employees.clear();

    for (Team * t : teams) {
        delete t;
    }

    teams.clear();
}

如果使用智能指针是更好的实践,那么在我的情况下我应该使用unique或shared指针?我已经阅读了有关智能指针的内容,它们会在作用域结束时删除分配的内存,但我真的很困惑什么时候结束。这将发生在公司构造函数被调用时吗?
提前感谢您的回答,如果我的问题看起来很愚蠢,请原谅。

3
unique_ptr可能更好,但你为什么首先使用指针呢?为什么不直接使用例如 std::vector<Employee> ? - lisyarus
因为员工的任何变动都应该对团队产生影响。 - Ivaylo Hristov
1
对于您目前的方法,请查阅“大三法则”(或“四分之三法则”或“五法则”)。但是,尽可能使用智能指针。在这种情况下,std::vector<Employee> 是您的朋友。 - Swordfish
1
因为任何对员工的更改都应该影响到团队。然后,您可以在团队中拥有指向std::vector<Employee> employees;元素的非所有权原始指针。只需确保在解雇时两者都被删除^^ - Swordfish
3个回答

3

如果您不知道自己在做什么,请永远不要在代码逻辑中编写delete。这是规则。一旦违反了这个规则,请预计会发生各种问题。换句话说:始终使用std容器+智能指针。

独占指针:是一个简单的包装器,它会在其超出作用域时删除其下方的资源。您不能复制独占指针,因为那么谁将持有指针的所有权呢?谁来删除它?只有1个对象应该能够删除它。

共享指针:使复制成为可能,但代价是具有(原子)计数器,计算引用相同指针的对象数量。当计数器归零时,删除指针下的资源。


2

这是一种正确的释放内存的方式吗?

假设容器是通过new操作符填充的,就像这样:

employees.push_back(new Employee());
teams.push_back(new Team());

是的,这是清理这些资源的正确方法。

...如果它将提供与使用智能指针自动化该过程相同的结果。

通常是的。您可以调整智能指针执行其管理的资源删除工作的方式,但默认行为对于您的情况是正确的(调用delete)。

我应该使用unique_ptr还是shared_ptr?

首选应该是std::unique_ptr。它比std::shared_ptr更高效,并且对其管理的对象如何使用更加严格。但是这也有限制,例如具有std::unique_ptr数据成员(如您的情况中的Company)的类无法复制。改用std::shared_ptr可以缓解此问题,但具有非常不同的语义 - 然后复制Company对象意味着共享团队和员工。这可能不是您想要的。解决此问题的一种方法是使用std::unique_ptr,通过所谓的虚构造函数Employee :: clone()Team :: clone()成员函数实现Company的复制和复制赋值构造函数。

它们在作用域结束时删除分配的内存,但是我真的很困惑什么时候会结束。

一个例子:

void someFunction()
{
    Company c;

    // stuff...

} // the scope ends here, ~Company will be called

假设容器是用 new 操作符填充的,就像这样 (...) 那么,是的,这是清理这些资源的正确方法。但至少你展示的方式不是,因为在构造函数中,如果 new 过程中发生异常,则不会为创建的向量元素调用删除 : /。通常,类接口不应通过指针移交对象所有权(我说的是“addEmployee”和“addTeam”方法)。 - Michał Łoś

1
这是释放内存的正确方法吗?
析构函数没问题,假设所有存储的指针都是使用new-expression分配的,并且假设它们都是有效的,即不会悬空。
清除向量是多余的。向量将立即被销毁,因此它们的元素将被清除。
但是,您的类很可能有问题。它是可复制的,如果您进行副本并且不从其中一个副本中删除指针,则这些对象的析构函数将删除相同的指针,这将导致未定义的行为。
您可以通过使用智能指针来解决此问题以及使析构函数更简单(您只需使用隐式析构函数)。
我应该使用unique或shared指针吗?
是的,你应该。
您的代码演示了唯一拥有权 - Company唯一拥有Employee和Team - 因此最简单的更改是使用unique指针。
我已经阅读了关于智能指针的内容,它们在范围结束时删除分配的内存,但我真的很困惑什么时候会结束。这会在调用公司的析构函数时发生吗?
它是智能指针的析构函数,用于删除指针。
当智能指针是局部变量时,在作用域结束时会被销毁。就像如果您的Company是一个局部变量,它将在作用域结束时被销毁,其析构函数将运行。
当智能指针是成员变量时,它将在超级对象被销毁时被销毁。在您的情况下,您将拥有一个智能指针的向量,此时当向量被销毁或从向量中擦除智能指针时,智能指针将被销毁。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接