C ++如何避免访问尚未初始化的对象的成员

时间:2017-02-27 17:03:01

标签: c++ containers

在程序中传递对象有什么好的做法选项,避免访问非初始化的成员变量。

我写了一个小例子,我认为很好地解释了这个问题。

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

5 个答案:

答案 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::optionalboost::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)

  

在程序中传递对象有什么好的做法选项,避免访问非初始化的成员变量。

好的做法是初始化构造函数中的所有内容。

辩论更好的做法是初始化构造函数中的所有内容无法修改任何成员。