只是一个简单的问题:
如果我有一个简单的矢量类:
class Vector
{
public:
float x;
float y;
float z;
};
RAII概念在这里也不适用吗?即提供构造函数以将所有值初始化为某些值(以防止使用未初始化的值)。
编辑或提供一个构造函数,该构造函数明确要求用户在对象被禁用之前初始化成员变量。
即
class Vector
{
public:
float x;
float y;
float z;
public:
Vector( float x_, float y_, float z_ )
: x( x_ ), y( y_ ), z( z_ )
{ // Code to check pre-condition; }
};
RAII是否应该用于帮助程序员忘记在使用之前初始化值,还是开发人员负责?
或者看待RAII的方式是错误的吗?
我故意让这个例子变得非常简单。我真正的问题是回答,例如,复合类,如:
class VectorField
{
public:
Vector top;
Vector bottom;
Vector back;
// a lot more!
};
正如你所看到的......如果我必须编写一个构造函数来初始化每个成员,那就非常繁琐了。
思想?
答案 0 :(得分:6)
RAII中的“R”代表资源。并非一切都是资源。
许多类,例如std :: vector,都是自我初始化的。你不必担心这些。
POD类型不自我初始化,因此将它们初始化为一些有用的值是有意义的。
答案 1 :(得分:4)
由于Vector
类中的字段是内置类型,为了确保它们已初始化,您必须在构造函数中执行此操作:
class Vector
{
public:
float x;
float y;
float z;
Vector() : x(0.0), y( 0.0), z( 0.0) {}
};
现在,如果您的字段是正确编写的类,则它们应自动初始化(并在必要时进行清理)。
在某种程度上,这与RAII相似,因为RAII意味着对象可以自动获取和清理资源(内存,句柄等)。
答案 2 :(得分:3)
我不确切地说RAII适用于此。记住字母代表的含义:资源获取是初始化。您没有在此获得资源,因此RAII不适用。
您可以为Vector
提供默认构造函数;这将使您无需显式初始化VectorField
的所有成员。编译器会插入代码来为您执行此操作。
答案 3 :(得分:3)
当您需要进行显式清理时,可以使用RAII模式,并希望在清除另一个对象的同时进行清理。对于内存分配/释放,关键部分进入/退出,数据库连接等,可能会发生这种情况。在您的示例中,“浮动”会自动清除,因此您无需担心它们。但是,假设您调用了以下函数来获取向量:
Vector* getMeAVector() {
Vector *v = new Vector();
// do something
return v;
}
并说调用者有责任删除返回的向量。如果您通过以下方式调用此代码:
Vector *v = getMeAVector();
// do some stuff with v
delete v;
你必须记住释放矢量。如果“stuff”是一长串代码,可能会抛出异常,或者在那里有一堆返回语句,那么你必须在每个出口点释放向量。即使你这样做,通过添加另一个“return”语句或调用某个抛出异常的库来维护代码的人也许不会。相反,你可以写一个这样的类:
class AutoVector
{
Vector *v_;
public:
AutoVector(Vector *v) : v_(v) {}
~AutoVector() { delete v_; }
};
然后,您可以像这样获得矢量:
Vector *v = getMeAVector();
AutoVector av(v);
// do lots of complicated stuff including throwing exceptions, multiple returns, etc.
然后您不必再担心删除矢量了,因为当av超出范围时,它将被自动删除。如果需要,你可以编写一个小宏来使“AutoVector av(v)”语法更好一些。
这是一个人为的例子,但是如果周围的代码很复杂,或者它可以抛出异常,或者有人出现并在中间添加“返回”语句,那么“AutoVector”将会很好自动释放内存。
您可以使用“自动”类进行同样的操作,该类进入其ctor中的关键部分并退出其dtor等。
答案 4 :(得分:0)
如果您不编写构造函数,编译器将生成 默认构造函数,并将这些值设置为默认值(未初始化的值)。自己提供默认构造函数并初始化值,这将是您最好的方法。我不认为这样做太复杂了。不要太懒: - )