检查变量是否已初始化

时间:2011-07-25 20:36:54

标签: c++ class

似乎这样会有重复,但也许它很明显它没有被问过......

这是检查C ++类中是否初始化变量(非指针)的正确方法吗?

class MyClass
{
    void SomeMethod();

    char mCharacter;
    double mDecimal;
};

void MyClass::SomeMethod()
{
    if ( mCharacter )
    {
        // do something with mCharacter.
    }

    if ( ! mDecimal )
    {
        // define mDecimal.
    }
}

13 个答案:

答案 0 :(得分:29)

无法检查变量的内容是否未定义。你可以做的最好的事情是分配一个signal / sentinel值(例如在构造函数中),以指示需要进行进一步的初始化。

答案 1 :(得分:21)

未定义的变量将导致编译错误。

您要问的是检查已初始化。但是初始化只是一个值,你应该在构造函数中选择和赋值。

例如:

class MyClass
{
    MyClass() : mCharacter('0'), mDecimal(-1.0){};
    void SomeMethod();

    char mCharacter;
    double mDecimal;
};

void MyClass::SomeMethod()
{
    if ( mCharacter != '0')
    {
        // touched after the constructor
        // do something with mCharacter.
    }

    if ( mDecimal != -1.0 )
    {
        // touched after the constructor
        // define mDecimal.
    }
}

当然,您应该初始化为逻辑上下文中的某个默认值。

答案 2 :(得分:14)

根据您的应用程序(特别是如果您已经使用了提升),您可能需要查看boost::optional

(更新:从C ++ 17开始,可选现在是标准库的一部分,如std::optional

它具有您正在寻找的属性,跟踪插槽是否实际包含值。默认情况下,它构造为不保存值并计算为false,但如果计算结果为true,则允许取消引用它并获取包装值。

class MyClass
{
    void SomeMethod();

    optional<char> mCharacter;
    optional<double> mDecimal;
};

void MyClass::SomeMethod()
{
    if ( mCharacter )
    {
        // do something with *mCharacter.
        // (note you must use the dereference operator)
    }

    if ( ! mDecimal )
    {
        // call mDecimal.reset(expression)
        // (this is how you assign an optional)
    }
}

更多示例位于the Boost documentation

答案 3 :(得分:8)

使用C ++ 17 ,您可以使用std::optional检查变量是否已初始化:

#include <optional>
#include <iostream>  // needed only for std::cout

int main() {
    std::optional<int> variable;

    if (!variable) {
        std::cout << "variable is NOT initialized\n";
    }

    variable = 3;

    if (variable) {
        std::cout << "variable IS initialized and is set to " << *variable << '\n';
    }

    return 0;
}

这将产生输出:

variable is NOT initialized
variable IS initialized and is set to 3

要在您提供的代码中使用std::optional,您必须包含<optional>标准库标头并将std::optional<...>添加到相应的变量声明中:

#include <optional>

class MyClass
{
    void SomeMethod();

    std::optional<char> mCharacter;
    std::optional<double> mDecimal;
};

void MyClass::SomeMethod()
{
    if ( mCharacter )
    {
        std::cout << *mCharacter;  // do something with mCharacter.
    }

    if ( ! mDecimal )
    {
        mDecimal = 3.14159;  // define mDecimal.
    }
}

答案 4 :(得分:3)

默认情况下,您无法知道变量(或指针)是否已初始化。但是,由于其他人都在告诉你“简单”或“正常”的方法,我会给你一些别的想法。这就是你能够跟踪这样的事情的方式(不,我个人永远不会这样做,但也许你有不同的需求)。

class MyVeryCoolInteger
{
public:
    MyVeryCoolInteger() : m_initialized(false) {}

    MyVeryCoolInteger& operator=(const int integer)
    {
        m_initialized = true;
        m_int = integer;
        return *this;
    }

    int value()
    {
        return m_int;
    }

    bool isInitialized()
    {
        return m_initialized;
    }

private:
    int m_int;
    bool m_initialized;
};

答案 5 :(得分:3)

使用C ++ - 11,您可以考虑使用智能指针存储变量。考虑这个MVE,toString()行为取决于bar被初始化:

#include <memory>
#include <sstream>

class Foo {

private:
    std::shared_ptr<int> bar;

public:
    Foo() {}
    void setBar(int bar) {
        this->bar = std::make_shared<int>(bar);
    }
    std::string toString() const {
        std::ostringstream ss;
        if (bar)           // bar was set
            ss << *bar;
        else               // bar was never set
            ss << "unset";
        return ss.str();
    }
};

使用此代码

Foo f;
std::cout << f.toString() << std::endl;
f.setBar(42);
std::cout << f.toString() << std::endl;

产生输出

unset
42

答案 6 :(得分:2)

没有合理的方法来检查值是否已初始化。

如果您关心某些内容是否已初始化,而不是尝试检查它,请将代码放入构造函数中,以确保它们始终初始化并完成它。

答案 7 :(得分:1)

由于MyClass是POD类类型,当您创建MyClass的非静态实例时,那些非静态数据成员将具有不确定的初始值,所以不,这不是一种有效的方式检查它们是否已被初始化为特定的非零值...您基本上假设它们将被零初始化,但事实并非如此,因为您没有在构造函数中对它们进行值初始化。

如果要对类的非静态数据成员进行零初始化,最好创建一个初始化列表和类构造函数。例如:

class MyClass
{
    void SomeMethod();

    char mCharacter;
    double mDecimal;

    public:
        MyClass();
};

MyClass::MyClass(): mCharacter(0), mDecimal(0) {}

上面构造函数中的初始化列表将数据成员初始化为零。您现在可以正确地假设mCharactermDecimal的任何非零值必须由您在代码中的其他位置专门设置,并包含可以正确操作的非零值。

答案 8 :(得分:1)

如果您的意思是如何检查成员变量是否已初始化,您可以通过在构造函数中指定它们的sentinel值来完成此操作。选择哨兵值作为在该变量的正常使用中永远不会出现的值。如果变量整个范围被认为是有效的,则可以创建一个布尔值来指示它是否已被初始化。

#include <limits>

class MyClass
{
    void SomeMethod();

    char mCharacter;
    bool isCharacterInitialized;
    double mDecimal;

    MyClass()
    : isCharacterInitialized(false)
    , mDecimal( std::numeric_limits<double>::quiet_NaN() )
    {}


};


void MyClass::SomeMethod()
{
    if ( isCharacterInitialized == false )
    {
        // do something with mCharacter.
    }

    if ( mDecimal != mDecimal ) // if true, mDecimal == NaN
    {
        // define mDecimal.
    }
}

答案 9 :(得分:0)

C ++语言中无法检查变量是否已初始化(尽管具有构造函数的类类型将自动初始化)。

相反,您需要做的是提供将类初始化为有效状态的构造函数。静态代码检查器(可能还有一些编译器)可以帮助您在构造函数中找到缺少的变量。这样你就不会担心处于虚假状态,并且你方法中的if检查可以完全消失。

答案 10 :(得分:0)

例如,如果您使用字符串而不是字符,则可以执行以下操作:

    //a is a string of length 1
    string a;
    //b is the char in which we'll put the char stored in a
    char b;
    bool isInitialized(){
      if(a.length() != NULL){
        b = a[0];
        return true;
      }else return false;
    }

答案 11 :(得分:0)

您可以在断言中引用该变量,然后使用-fsanitize=address

进行构建
void foo (int32_t& i) {
    // Assertion will trigger address sanitizer if not initialized:
    assert(static_cast<int64_t>(i) != INT64_MAX);
}

这将导致程序使用堆栈跟踪可靠地崩溃(与未定义的行为相反)。

答案 12 :(得分:0)

如果您不喜欢boost和c ++ 17,则可以使用google c ++ lib Abseil来实现。 absl :: optional就像std :: optional一样。

#include <absl/types/optional.h>
class MyClass
{
    void SomeMethod();

    absl::optional<char> mCharacter;
    absl::optional<double> mDecimal;
};

void MyClass::SomeMethod()
{
    if (mCharacter)
    {
        // do something with mCharacter.
    }

    if (!mDecimal)
    {
        // define mDecimal.
    }
}