C++ 析构函数多次销毁对象

C++ destructor destroys objects couple times

我是 C++ 的新手,所以有一个问题。

有C++代码:

class Test
{        
    public:
        std::string name;
        Test(){};
        Test(std::string name) {
        
            std::cout << "Create " << name << '\n';
            Test::name = name;
        };
        ~Test() {std::cout << "Destroy " << name << '\n';} 
};

std::vector<Test> test {Test("one"), Test("two"), Test("three")};

void main()
{
    for (auto i : test)
        std::cout << i.name << '\n';
    
    std::cout << "Clear\n";
    test.clear();
}

这是输出:

Create one
Create two
Create three
Destroy three
Destroy two
Destroy one
one
Destroy one
two
Destroy two
three
Destroy three
Clear
Destroy one
Destroy two
Destroy three

为什么编译器多次销毁 vector 中的对象,什么时候必须销毁一次?该代码有什么问题?

使用默认选项编译 Microsoft cl.exe x64。

What wrong in that code?

main 必须 return int。一定不能returnvoid。要解决此问题,请将 return 类型更改为 int

除此之外,您的期望是错误的。

当您创建变量或临时对象时,它会被销毁。当您创建一个对象的副本时,副本和原始对象最终都会被销毁(涉及内存泄漏的情况除外)。

因为在for循环中:

for (auto i : test)
    std::cout << i.name << '\n';

您实际上是在 std::vector<Test> test 中创建 Test 元素的另一个副本,而不是元素本身,所以这就是它创建(并销毁另一个副本)

的原因

将您的 for 循环更改为:

for (auto &i : test) // reference:
    std::cout << i.name << '\n';

生产:

Create one
Create two
Create three
Destroy three
Destroy two
Destroy one
one
two
three
Clear
Destroy one
Destroy two
Destroy three

这是你所期望的:

Try it live(包括所有正确的headers)


此外,main() returns 和 int,因此请将您的 main() 定义更改为:

int main()

int main() 不是 void main()。你的编译器应该已经警告过你了。确保启用编译器警告。

for (auto i : test) 制作对象的 copy。您可能想要 for (auto&& i : test)for (auto const& i : test)。请注意 auto&&Test&& 的含义略有不同,因为 auto&& 遵循 模板 规则。

代码忽略了隐式复制构造函数。在这里我添加了一个,这应该有助于理解正在发生的事情:

#include <iostream>
#include <string>
#include <utility>
#include <vector>

struct Test {
    std::string name;

    ~Test() {
        std::cout << "Destroy " << name << "\n";
    }

    Test() {
        std::cout << "Default-Create (empty)\n";
    }

    Test(std::string name_) : name{std::move(name_)} {
        std::cout << "Create " << name << "\n";
    }

    Test(Test const& other) : name{other.name + "!"} {
        std::cout << "Create-by-Copy " << name << "\n";
    }
};

int main() {
    auto test = std::vector<Test>{Test{"one"}, Test{"two"}, Test{"three"}};

    for (auto i : test)
        std::cout << i.name << "\n";

    std::cout << "Clear\n";
    test.clear();
}

让我们添加一个复制构造函数(并使用较小的测试用例,以减少冗长),看看会发生什么...

#include <iostream>
#include <string>
#include <vector>

class Test
{        
    public:
        std::string name;
        Test(){};
        Test(std::string name) : name(name) {        
            std::cout << "New " << name << '\n';
        }
        Test(const Test& other) : name("Copy of " + other.name) {
            std::cout << "Copied " << other.name << '\n';
        }
        ~Test() {std::cout << "Destroy " << name << '\n';} 
};

std::vector<Test> test {Test("Original") };

int main()
{
    std::cout << "Loop:\n";
    for (auto i : test)
        std::cout << "This is " << i.name << '\n';   
    std::cout << "Clear\n";
    test.clear();
}

这会产生

New Original
Copied Original
Destroy Original
Loop:
Copied Copy of Original
This is Copy of Copy of Original
Destroy Copy of Copy of Original
Clear
Destroy Copy of Original

解释:

New Original -- The object in the initialzer list
Copied Original -- Here it gets copied into the vector
Destroy Original -- The original is destroyed along with the initializer list
Loop:
Copied Copy of Original -- Copied into the loop variable
This is Copy of Copy of Original -- Printing in the loop
Destroy Copy of Copy of Original -- The temporary loop object is destroyed
Clear
Destroy Copy of Original -- Clearing the vector

如果循环引用,i 将引用向量中的对象而不是它的副本 - 只需将一行更改为

for (auto& i : test)

将输出更改为

New Original
Copied Original
Destroy Original
Loop:
This is Copy of Original
Clear
Destroy Copy of Original

并且您可以通过直接在向量中创建对象来摆脱进一步的复制:

int main()
{
    std::vector<Test> test;
    test.emplace_back("Original");
    std::cout << "Loop:\n";
    for (auto& i : test)
        std::cout << "This is " << i.name << '\n';   
    std::cout << "Clear\n";
    test.clear();
}

输出:

Original
Loop:
This is Original
Clear
Destroy Original