我的问题实际上是两部分的。快速前言:我正在学习C ++并且来自C#/ .NET背景。目前,我正在尝试了解对象的生命周期,下面发布的结果对我来说没有意义。我认为这可能与创建匿名实例有关吗?
问题1:处理析构函数中的所有成员是否是一个好主意,因为它可能是一个“空对象”?
问题2:如何防止这种情况发生? /如何设计我的对象以使用此功能?
问题3:这是“正确”/“正确”的方式吗?
问题4:在最后一段代码中查看评论
#include <stdio.h>
class A
{
public:
A()
{
printf("Constructor A\n");
}
~A()
{
printf("Destructor A\n");
}
};
class B
{
public:
B()
{
a = A();
printf("Constructor B\n");
}
~B()
{
printf("Destructor B\n");
}
private:
A a;
};
int main(int argc, char* argv[])
{
B b;
b = B();
printf("Done");
// Breakpoint
/*
Output:
Constructor A
Constructor A
Destructor A
Constructor B
Constructor A
Constructor A
Destructor A
Constructor B
Destructor B
Destructor A
*/
}
另一个来自我目前正在进行的项目的例子。
#include <stdio.h>
class Mesh
{
public:
Mesh()
{
printf("Constructing Mesh with data %d\n", data);
}
Mesh(int d)
{
data = d;
printf("Constructing Mesh with data %d\n", data);
}
~Mesh()
{
printf("Destructing Mesh with data %d\n", data);
}
private:
int data = 0;
};
class Game
{
public:
Game()
{
printf("Game constructor\n");
}
~Game()
{
printf("Game destructor\n");
}
void init()
{
int cool_data = 3;
mesh = Mesh(cool_data);
}
private:
Mesh mesh;
};
int main(int argc, char* argv[])
{
Game game = Game();
game.init();
printf("");
// Breakpoint
/*
Output:
Constructing Mesh with data 0 <-- I assume this comes from the private member declaration in the Mesh class? So declaration means initialization?
Game constructor
Constructing Mesh with data 3 <-- Okay that's what I expected since I'm creating a new instance of the Mesh class with "3" passed in
Destructing Mesh with data 3 <-- Why is the instance we JUST created immediately being destructed?
*/
}
答案 0 :(得分:1)
在你的第一个例子中,我们看到很多构造和破坏“A”对象,如果你不熟悉C ++,这些对象似乎很神秘。你的B类有一个私人A变量“a”。首次将构造函数调用到B类时,将默认构造此对象。这是你看到的第一个“构造函数A”打印输出。下一个打印输出来自这里对A构造函数的调用:
B()
{
a = A(); //A() calls the A constructor and returns an r-value
printf("Constructor B\n");
}
将类A的已实例化对象“a”分配给由类A的默认构造函数调用返回的r值会导致在创建r值时打印“构造函数A”,并且“析构函数A“当r值本身被破坏时。可以通过创建复制和移动构造函数/运算符来更改这些行为,这将允许您指定这些语义的运行方式。查看此页面:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html或一本书(C ++ 11入门书很好),了解有关这些操作的更多信息。
按照上面的逻辑,当您将创建的B对象“b”分配给B类默认构造函数的行中返回的右值时:
b = B();
构造一个必须的新B对象 1)构造一个A对象 2)创建一个A r值 3)破坏A r值 4)构造一个B对象
最后两个打印语句只是你的B对象被破坏为主要出口。 B对象被破坏,它的成员也是A对象。你的第一个问题似乎是关于这种行为。看起来你问是否应该手动破坏类成员。如果您的类为其成员分配内存,则只能在C ++中完成。例如,如果不是创建本地A对象,则B中的构造函数执行此操作:
B()
{
a = new A();
printf("Constructor B\n");
}
...
private:
A* a;
然后你必须在你的析构函数中释放这个A *。只要您不使用new运算符或其他方式分配新内存,C ++将为您处理所有其他释放。
你的问题2和3是关于如何防止/使用对象构造以及构造/破坏对象的给定方式是对还是错。我想指出你的0/3/5规则。这基本上处理了您应该为给定类创建的构造函数数量。这是对http://en.cppreference.com/w/cpp/language/rule_of_three规则的一个非常简单的解释,但还有许多其他在线。
你的最后一个问题与你拥有的网格类有关,以及为什么你的一个变量被破坏了。同样,这与调用构造函数返回的r值有关。基本上,当您调用网格构造函数并将其返回值分配给变量“mesh”时:
void init()
{
int cool_data = 3;
mesh = Mesh(cool_data);
}
网格构造函数返回一个r值,它是Mesh类的一个对象,数据值为3;将r值复制到“网格”变量中并立即销毁,将“网格”变量作为其精确副本。同样,可以通过创建适当的构造函数和运算符来更改所有这些行为。