【问题标题】:C++ How to avoid access of members, of a object that was not yet initializedC ++如何避免访问尚未初始化的对象的成员
【发布时间】:2017-02-27 17:03:01
【问题描述】:

在程序中传递对象、避免访问未初始化的成员变量有哪些好的实践选项。

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

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

【问题讨论】:

  • 好的做法是初始化对象构造函数中的所有内容。
  • Ja 但这在这种情况下没有帮助。 ship1 左侧根本没有容器(为了保持我的示例语言)。
  • 怎么不呢?您可以初始化指向 nullptr 的指针。
  • 因为 op 正在谈论防止空指针取消引用,这与未初始化的成员无关。未初始化的成员很有可能在发布编译中不为空。好的做法是始终初始化。
  • @Jean 当然可以——如果指针未初始化,则无法测试它是否为空。

标签: c++ containers


【解决方案1】:

编译器无法确定您是否正在引入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

【讨论】:

  • 所以没有编译器标志可以警告我?
  • @newandlost 不,不适用于您提出的那个案例。对于更明显的情况,如int i; std::cout &lt;&lt; i &lt;&lt; std::endl;,有更复杂的警告
  • @newandlost 至于您的编辑尝试。不,// ^^^^^^^^^^^^^^^^^^^^^ 强调评论是有意识地放在那里的。
  • @newandlost 正如提到的评论强调我添加到您的构造函数示例中。
  • 为什么不初始化指向nullptr以外的指针?
【解决方案2】:

执行检查的一个好做法是使用std::optionalboost::optional

class Ship
{
public:
    Ship() : pContainer(nullptr) {}
    std::optional<container*> Container()
    {
        if(!pContainer)
            return {};
        return pContainer;
    }
private:
    container* pContainer;
};

它会迫使你(或者更好:提供一个坚定的提醒)检查你的 getter 的结果:

std::optional<container*> container = ship1.Container();
container->otherInfo; // will not compile
if(container)
    (*container)->otherInfo; // will compile

如果您使用指针,您将总是需要检查操作结果。我的意思是,使用可选的情况更明确,并且作为程序员的您忘记检查结果的可能性较小。

【讨论】:

  • 有点牵强,但好吧......(我没有投反对票,你的回答是合法的)
  • 我也喜欢你的回答。 πάντα ῥεῖ 给的那个也不错。
  • 我只能通过 std::experimental::optional 包含可选的。实验是否意味着它不安全?
  • @newandlost,它是新添加到 c++17 中的。它应该是稳定的,未来的编译器会支持它。
  • 我赞成您的答案,因为我真的很喜欢它,但接受了 πάντα ῥεῖ 的答案,因为它不需要“最新”编译器。但也许你的会在几年后成为标准。我对我来说似乎很省钱,因为如果指针有效,您会强制用户使用 if 语句进行检查。 @πάντα ῥεῖ 和 AMA,如果我们合并您的两个答案以确保完整性,您认为是否可以?
【解决方案3】:

您似乎正在寻找一种方法来制作您的代码

bool info = ship1.pContainer->otherInfo;

即使pContainer 可能为空也可以工作。

您可以使用一个哨兵对象,其中包含一些默认数据:

container default_container;
default_container.otherInfo = false; // or whatever the default is

然后使用指向哨兵对象的指针而不是空指针:

//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;

如果你使用它,你应该确保哨兵对象不能被破坏(例如,使其成为static 成员,或单例)。

另外,在构造函数中,初始化你的指针,使它们指向哨兵对象:

class Ship
{
public: Ship(): pContainer(&default_container) {}
    ...
};

【讨论】:

  • “即使pContainer 可能为空也可以工作。” 什么时候应该将pContainer 初始化为null 值?
  • 它永远不应该为空。我用一个特殊的值替换了nullptr,它可以被取消引用。
  • 也喜欢那个!
【解决方案4】:

我找到了一个额外的解决方案。诚然,这并没有阻止对未初始化对象的访问,但至少程序崩溃并返回错误消息,这使我们能够纠正我们的错误。 (此解决方案特别适用于 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&lt;_Tp, _Dp&gt;::pointer = container*]: Assertion 'get() != pointer()' failed.

因此告诉我们应该包含if(ship1.upContainer) 检查。

【讨论】:

    【解决方案5】:

    在程序中传递对象、避免访问未初始化的成员变量有哪些好的实践选项。

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

    值得商榷的更好做法是在构造函数中初始化所有内容并且不提供修改任何成员的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-17
      • 1970-01-01
      • 1970-01-01
      • 2019-03-19
      相关资源
      最近更新 更多