在程序中传递对象有什么好的做法选项,避免访问非初始化的成员变量。
我写了一个小例子,我认为很好地解释了这个问题。
#include <vector>
using namespace std;
class container{public:container(){}
vector<int> LongList;
bool otherInfo;
};
class Ship
{
public:Ship(){}
container* pContainer;
};
int main()
{
//Create contianer on ship1
Ship ship1;
ship1.pContainer = new container;
ship1.pContainer->LongList.push_back(33);
ship1.pContainer->otherInfo = true;
Ship ship2;
//Transfer container from ship1 onto ship2
ship2.pContainer = ship1.pContainer;
ship1.pContainer = 0;
//2000 lines of code further...
//embedded in 100 if statements....
bool info = ship1.pContainer->otherInfo;
//and the program crashes
return 0;
}
答案 0 :(得分:2)
编译器无法确定您是否正在引入示例中显示的undefined behavior。所以没有办法确定指针变量是否已初始化,除了用“特殊值”初始化它。
在程序中传递对象有什么好的做法选项,避免访问非初始化的成员变量。
最佳做法是始终初始化指针,并在解除引用之前进行检查:
class Ship {
public:
Ship() : pContainer(nullptr) {}
// ^^^^^^^^^^^^^^^^^^^^^
container* pContainer;
};
// ...
if(ship1.pContainer->LongList) {
ship1.pContainer->LongList.push_back(33);
}
至于你的comment:
所以没有可以警告我的编译器标志?
有更简单明显的情况,编译器可能会给你一个警告:
int i;
std::cout << i << std::endl;
吐出
main.cpp: In functin 'int main()':
main.cpp:5:18: warning: 'i' is used uninitialized in this function [-Wuninitialized]
std::cout << i << std::endl;
^
请参阅Live Demo
答案 1 :(得分:2)
执行检查的一个好方法是使用std::optional
或boost::optional
。
class Ship
{
public:
Ship() : pContainer(nullptr) {}
std::optional<container*> Container()
{
if(!pContainer)
return {};
return pContainer;
}
private:
container* pContainer;
};
它会迫使你(或者更好:提供一个坚定的提醒)来检查你的吸气者的结果:
std::optional<container*> container = ship1.Container();
container->otherInfo; // will not compile
if(container)
(*container)->otherInfo; // will compile
如果使用指针, 总是需要检查 操作结果。我的意思是,在可选的情况下, 更明确 ,并且程序员忘记检查结果的可能性会降低。
答案 2 :(得分:1)
您似乎正在寻找一种制作代码的方法
bool info = ship1.pContainer->otherInfo;
即使pContainer
可能为空,也能正常工作。
您可以使用包含一些默认数据的sentinel对象:
container default_container;
default_container.otherInfo = false; // or whatever the default is
然后使用指向sentinel对象的指针而不是空指针:
//Transfer container from ship1 onto ship2
ship2.pContainer = ship1.pContainer;
ship1.pContainer = &default_container; // instead of 0
//2000 lines of code further...
//embedded in 100 if statements....
bool info = ship1.pContainer->otherInfo;
如果你使用它,你应该确保不能销毁sentinel对象(例如,使其成为static
成员或单身人士。)
此外,在构造函数中,初始化指针,使它们指向sentinel对象:
class Ship
{
public: Ship(): pContainer(&default_container) {}
...
};
答案 3 :(得分:0)
我找到了一个额外的解决方案。诚然,它无法阻止未初始化对象的访问,但至少程序崩溃并返回错误消息,这使我们能够纠正错误。 (此解决方案特别适用于g ++编译器。)
首先设置编译器标志_GLIBCXX_DEBUG
。然后使用unique_ptr
代替裸指针。
#include <vector>
#include <iostream>
#include <memory>
using namespace std;
class container{
public:container(){}
int otherInfo = 33;
};
class Ship
{
public:Ship(){}
std::unique_ptr<container> upContainer;
};
int main()
{
Ship ship1;
cout<<ship1.upContainer->otherInfo<<endl;
return 0;
}
此代码将产生错误:
std::unique_ptr<_Tp, _Dp>::pointer = container*]: Assertion 'get() != pointer()' failed.
因此告诉我们,我们应该包括if(ship1.upContainer)
支票。
答案 4 :(得分:-1)
在程序中传递对象有什么好的做法选项,避免访问非初始化的成员变量。
好的做法是初始化构造函数中的所有内容。
辩论更好的做法是初始化构造函数中的所有内容和无法修改任何成员。