我有一个有两种状态的类,不同的成员只适用于一种状态或另一种状态。
哪种方式更好:
E.g。
class Foo {
public:
enum State { A, B };
// Constructor
// Option 1: Only initialize first state
Foo(int a1) : _state(A), _a1(a1) {}
// ... or ...
// Option 2: initialize every member
Foo(int a1) : _state(A), _a1(a1), b1(-1), b2(-1) {}
State getState() const { return _state; }
// Only applicable if State is A
int getA1() const { return _a1; } // Might also add assert that State is A
// Only applicable if State is B
int getB1() const { return _b1; } // Might also add assert that State is B
int getB2() const { return _b2; } // Might also add assert that State is B
private:
State _state;
int _a1;
int _b1;
int _b2;
};
答案 0 :(得分:3)
从未初始化的变量读取是未定义的行为,因此如果您使用选项1然后有人调用getB1()
或getB2()
,则您有未定义的行为。
选项1 没有任何内在错误,只要您清楚地记录调用这些getter可能会调用未定义的行为以及可能发生的情况。这样,您就可以将确定已定义行为的负担转移到此类的使用者身上。
您还可以存储指示它们是否已初始化的标志,如果在初始化之前尝试读取则抛出异常。这样你就会得到一个明确定义的错误而不是UB。 (你也可以在这里使用boost::optional<int>
,它负责为你提供这个额外的标志。)
考虑所有这些要点,使用&#34;虚拟&#34;值可能是首选,因为不存在未定义行为的风险并导致更简单的实现。 (如果您使用虚拟值,请确保提供静态常量值,以便调用者可以进行比较以查看该值是否已设置。)
答案 1 :(得分:2)
这可能是使用union作为成员的情况。您可以使用union结构来节省内存空间,同时让断言检查要使用的union成员的状态。
struct BMembers {
int _b1;
int _b2;
};
union MyUnion {
int _a1;
BMembers members;
};
class Foo {
public:
enum State { A, B };
Foo(int a1) : _state(A), myUnion._a1(a1) {}
State getState() const { return _state; }
// Only applicable if State is A
int getA1() const { return myUnion._a1; } // Might also add assert that State is A
// Only applicable if State is B
int getB1() const { return myUnion._b1; } // Might also add assert that State is B
int getB2() const { return myUnion._b2; } // Might also add assert that State is B
private:
State _state;
MyUnion myUnion;
};
答案 2 :(得分:1)
初始化所有内容是一种很好的做法。也许你的对象没有真正的价值,可以算作“未初始化”,这会破坏你的代码。 作为解决方案,请考虑使用单独的类来定义每个状态。这样,您可以更好地记录每个州所需的内容,并且,根据您的成员的大小,可以通过仅存储您需要的内容来节省空间:
class Foo{
public:
enum State{A,B};
virtual State getState() const = 0;
virtual int getA1() const = 0;
virtual int getB1() const = 0;
virtual int getB2() const = 0;
};
class Foo_A : public Foo{
public:
Foo_A(int a1) : _a1(a1) {} // State implicit
State getState() const {return A;}
int getA1() const {return _a1;}
int getB1() const {throw "Bad Call";} // For simplicity. You should use a class derived from std::exception;
int getB2() const {throw "Bad Call";}
private:
int _a1;
};
class Foo_B : public Foo{
public:
Foo_B(int b1, int b2) : _b1(b1), _b2(b2) {}
State getState() const {return B;}
int getA1() const {throw "Bad Call";}
int getB1() const {return _b1;}
int getB2() const {return _b2;}
private:
int _b1;
int _b2;
};
答案 3 :(得分:0)
我希望将一切都投入使用是好的做法。
对于某个州,请将这些值设为无效的值
PS:对于初始化列表,按照与声明相同的顺序执行它们。
答案 4 :(得分:0)
这取决于您的具体情况。在声明中初始化每个变量是一种很好的做法。 在一个大程序中,我们可能忘记初始化变量,这可能导致代码转储。 稍后我们将需要额外的努力来找出这些初始化问题。 因此,在声明本身初始化变量是一种很好的编码实践。 为此,我们对类使用构造函数。
在这种特殊情况下,第二种选择更好。如果你调用getB1()或getB2(),它们会返回垃圾值。
如果那是确定它不会被召唤,那就可以了。但它更好地初始化它们。
答案 5 :(得分:0)
让任何成员未初始化是一个绝佳的机会,任何有足够访问权限的功能(成员,朋友等)在初始化之前尝试访问该成员。这会导致未定义的行为 - 通常在构造函数之外的代码中。由于问题的根本原因(构造函数中未初始化)和触发器(访问)位于代码的不同位置,这意味着难以识别的错误,因此很难纠正。
因此,一般情况下,最好初始化构造函数中的所有成员。
然而,这并不意味着某些成员“发明”价值。更好的做法是避免创建对象,直到构建它所需的所有信息都可用为止。