【问题标题】:Self assignment in initialisation初始化中的自赋值
【发布时间】:2014-05-25 07:17:55
【问题描述】:

根据我的上一个问题Initialisation of static variable with itself,让我们考虑以下新示例:

#include <iostream>

class B
{
  public:
    B()
    {
        std::cout << "B()" << std::endl;
        m = 17;
    }

    B(const B & o)
    {
        std::cout << "B(const B& o)" << std::endl;
        m = o.m;
    }
    B & operator=(const B & o)
    {
        std::cout << "B & operator=(const B & o)" << std::endl;
        m = o.m;
        return *this;
    }
    int m;
};

int main()
{
    B b  = b;
    std::cout << b.m << std::endl;
}

程序的输出是

B(const B& o)
1840828080

'b' 在这里使用未初始化,因为它在复制构造函数中用于构造自身。它导致未初始化的变量“m”,从而显示垃圾。为什么编译器在这里没有警告,'b' 未初始化使用(而 'int a = a' 产生这样的警告)。

Link to live example

【问题讨论】:

  • 您使用的是什么编译器/平台/操作系统,大多数编译器都有会引发此类警告的标志
  • @EdChum 他的实时示例显示带有-Wall-Wextra 的g++4.8 并没有警告它。
  • @JBL 添加-Weffc++ 标志将生成:main.cpp:6:5: warning: 'B::m' should be initialized in the member initialization list [-Weffc++]
  • 编译器不负责报告所有情况的警告,甚至不可能。编译器只是尽力检测这些情况。

标签: c++


【解决方案1】:

因为它是未定义的行为,编译器可能会也可能不会给出任何警告!在这种情况下,编译器不需要提供诊断(警告/错误)。

您的代码会调用未定义的行为,因为b(在= 的右侧)未初始化(如您所知)——读取其值会调用 UB。与this one基本相同。

【讨论】:

  • 当然编译器不需要警告。我只是想知道,为什么原始 int 会出现警告而不是类。 (对于相同的警告级别)。 '-weffc++' 是答案...
  • @meddle0106:我想知道-weffc++ 是怎么回答的。是的,它使编译器产生警告,但它仍然没有回答 “为什么原始 int 出现警告而不是类出现警告”,因为我确定你没有使用 -weffc++与原始 int。要知道答案(即差异的原因),您必须查看 GCC 的源代码。
  • 你是对的。它会产生警告。但是通过下面的示例,可以删除警告并且问题仍然存在。
【解决方案2】:

正如@Nawaz 所说,@kcm1700 警告不是编译器的要求。

至于为什么-weffc++ 这个标志似乎比-Wall -Wextra -pedantic 更严格,请参阅在线文档:http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html

您可以从文档中看到:

以下 -W... 选项不受 -Wall 影响。

-Weffc++(仅限 C++ 和 Objective-C++)警告违反以下来自 Scott Meyers 的有效 C++ 系列的风格指南 书籍:定义复制构造函数和赋值运算符 具有动态分配内存的类。更喜欢初始化 构造函数中的赋值。让 operator= 返回对 *这。当你必须返回一个对象时,不要试图返回一个引用。区分前缀和后缀形式的增量和 递减运算符。永远不要重载 &&、|| 或 ,。这个选项也 启用 -Wnon-virtual-dtor,这也是有效的 C++ 之一 建议。然而,检查被扩展以警告缺乏 在可访问的非多态基类中使用虚拟析构函数。

选择此选项时,请注意标准库头文件 不遵守所有这些准则;使用‘grep -v’过滤掉那些 警告。

因此,如果您添加 -weffc++ 标志,那么它将生成所需的警告:

g++-4.8 -std=c++11 -Weffc++ -Wall -Wextra -pedantic main.cpp && ./a.out

main.cpp: In constructor 'B::B()':

main.cpp:6:5: warning: 'B::m' should be initialized in the member initialization list [-Weffc++]

     B()

     ^

main.cpp: In copy constructor 'B::B(const B&)':

main.cpp:12:5: warning: 'B::m' should be initialized in the member initialization list [-Weffc++]

     B(const B & o)

     ^

live example及相关:Easy way find uninitialized member variables

对于您在 cmets 中修改的示例,clang 都能正确检测到,请参阅:http://coliru.stacked-crooked.com/a/d1a55f347ee928behttp://coliru.stacked-crooked.com/a/9676797c7d155b81

输出是:

main.cpp:27:12: warning: variable 'b' is uninitialized when used within its own initialization [-Wuninitialized]

    B b  = b;

      ~    ^

1 warning generated.

不同的编译器在检测此类问题方面具有不同的能力。

【讨论】:

【解决方案3】:

-Weffc++ 绝对不是答案!警告只是说,初始化应该进入初始化列表。如果你这样做,警告就会消失:

#include <iostream>

class B
{
  public:
    B() : m(17)
    {
        std::cout << "B()" << std::endl;
    }

    B(const B & o) : m(o.m)
    {
        std::cout << "B(const B& o)" << std::endl;
    }
    B & operator=(const B & o)
    {
        std::cout << "B & operator=(const B & o)" << std::endl;
        m = o.m;
        return *this;
    }
    int m;
};

int main()
{
    B b  = b;
    int i = i;

    std::cout << b.m << " " << i << std::endl;
}

live example 的编译器对简单变量 i 给出警告,但对类变量 b 没有。 所以这个问题仍然没有答案 - 为什么编译器会警告一个简单的类型,而不是一个更复杂的类型?

【讨论】:

    猜你喜欢
    • 2011-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-02
    • 1970-01-01
    • 2011-03-14
    • 1970-01-01
    相关资源
    最近更新 更多