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

时间:2016-05-31 19:39:56

标签: c# c++ constructor destructor temporary-objects

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

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

此外,我注意到在打印" lalala"之前,有些但不是所有的C ++析构函数都被调用。

为什么这个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");   
    }
}

2 个答案:

答案 0 :(得分:3)

C ++中缺少复制构造函数会增加报告的构造函数调用数和析构函数调用之间的不匹配。创建了大量副本(数量取决于编译器执行/允许的优化)。

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

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

  • C ++析构函数是同步的,而C#“析构函数”是异步的,可能永远不会运行(与C ++相比,当对象“超出范围”时绝对不行。)

答案 1 :(得分:3)

在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;
}