与 C# 相比,C++ 中的构造函数和析构函数

Constructors and Destructors in C++ compared to C#

我想用下面的代码创建 class "test" 的 5 个对象,每次创建时调用构造函数,将它们存储在一个向量中,打印 "lalalala" 一次,然后 运行 析构函数并销毁创建的对象。我希望析构函数为我创建的每个对象 运行 一次,不超过一次。

我认为以下示例中的 C++ 正在创建我不想要的对象的多个额外副本,否则它会调用析构函数的次数超过它应该调用的次数。我不知道。试过 gcc 和 clang 没有区别。尝试了堆栈和堆分配,以及 std::move。都产生相同的结果,比我要求的更多的析构函数调用。

我还注意到在我打印之前调用了一些但不是所有的 C++ 析构函数 "lalala"。

为什么这个 C++ 代码 运行 有 17 次析构函数:

#include <iostream>
#include <vector>

using namespace std;


class test
{
public:
    int t;
    test(int i)
    {
        t = i;
        cout << "Created instance " << t << ". \n";
    }
    ~test()
    {
        cout << "(*)Deleted instance " <<  t <<  ".\n";
    }
};

int main()
{
    vector <test> V;

    for(int i = 1; i <= 5; i++)
    {
        test D(i);
        V.push_back(D);
    }
   cout << "LALALLALA \n";
   return 0;
}

虽然此 C# 代码完全符合我的要求,但有 5 个构造函数和 5 个析构函数。

using System.IO;
using System;
using System.Collections.Generic;
class Program
{
    class test
    {
        public int t;
        public test(int i)
        {
            t = i;
            Console.Write ("Created instance ");
            Console.Write (t.ToString());
            Console.WriteLine(".");
        }

        ~test()
        {
            Console.Write( "(*)Deleted instance ");
            Console.Write(t.ToString());
            Console.WriteLine(".");
        }

    }
    static void Main()
    {
        List<test> lst = new List<test>();
        for(int i = 0; i < 5; i++)
        {
            test temp = new test(i);
            lst.Add(temp);
        }
        Console.WriteLine("LALALLALA \n");   
    }
}

C++ 中缺少复制构造函数导致报告的构造函数调用次数与析构函数调用次数不匹配。创建了很多副本(数量取决于编译器的优化 performed/allowed)。

请注意,您将苹果与橙子进行比较(两次):

  • C++ 对象默认是值类型,而 C# 类 是引用类型(并且 C# 值类型 - struct 不能有析构函数)。因此,如果您的 C++ 代码使用封装在某种智能指针中的指针(从而也避免了缺少复制构造函数的问题),您将获得相同的行为。

  • C++ 析构函数是同步的,而 C# "destructors" 是异步的,可能永远不会 运行(绝对不是当对象 "out of scope" 与 C++ 相比)。

在 C# 中,您的 List 存储对对象的引用。在 C++ 中,您实际上是在向量中存储对象。这意味着它们在放入向量时需要复制,并且在向量需要重新分配时也需要复制。这些副本中的每一个都是一个单独的对象(使用复制构造函数创建,您没有跟踪),并且将调用析构函数。

在 C++ 中与您在 C# 中所做的事情的近似是将指针(或智能指针)存储到堆分配的测试对象,而不是测试对象本身

#include <memory> // for shared_ptr

int main()
{
    std::vector<std::shared_ptr<test>> V;

    for(int i = 1; i <= 5; i++)
    {
        auto D = std::make_shared<test>(i);
        V.push_back(D);
    }
    std::cout << "LALALLALA \n";
    return 0;
}

一种首选的惯用 C++ 方法,如果您想确保只创建 5 个对象,但不必在堆上单独分配每个对象,则如下所示:

int main()
{
    std::vector<test> V;

    // ensure we can store 5 objects without reallocating
    V.reserve(5);

    for(int i = 1; i <= 5; i++)
    {
        // construct the object in place in the vector
        V.emplace_back(i);
    }
    std::cout << "LALALLALA \n";
    return 0;
}