在构造函数中调用的成员变量的析构函数 - 编译器错误?

Posted

技术标签:

【中文标题】在构造函数中调用的成员变量的析构函数 - 编译器错误?【英文标题】:Destructor of member variable called in constructor - compiler bug? 【发布时间】:2012-10-17 10:50:53 【问题描述】:

我在使用 Visual Studio 2012 时遇到问题。首先是 SSCCE:

class CacheImpl

public:
    float* m_cache;

    CacheImpl()
    
        m_cache=(float*)new float[1];
    

    ~CacheImpl()
            
        delete [] m_cache;
    
;

class Image 

public:
    Image() 
    ~Image() 
;

static const Image g_tmpImg;

class Filter
       

public:

    Filter() : m_img(Image())
    //Filter() : m_img(g_tmpImg) // <-- This variant works
    
        //Empty
    

private:

    CacheImpl m_cache;
    const Image &m_img;
;

int main()

    Filter f;
    return 0;

当运行这个(在调试模式下编译)时,我在 CacheImpl 中的删除上得到一个 CRT 断言,并查看 Filter() 的程序集列表或在 ~CacheImpl() 中设置断点表明 ~CacheImpl() 正在在过滤器构造函数的末尾调用,没有明显的原因(实际上,这在 VS2010 中不会发生)。而是为临时对象调用 ~Image(),而 VS2012 没有这样做。

在 VS2012 中编译它时,我收到警告“C4413:'Filter::m_img':引用成员被初始化为一个临时的,在构造函数退出后不会持续存在”。我理解这一点,但我期望一个悬空引用,而不是崩溃,因为错误的对象正在被破坏。我是否偶然发现了编译器错误,还是应该将其视为未定义的行为而不初始化对临时对象的引用?对于上下文,在我的真实代码中,当使用这样的构造函数创建 Filter 时,从不使用悬空引用。

【问题讨论】:

Rule of Zero:使用std::vector 是的,但这不是重点。内存分配只是为了暴露双重破坏。 @R.MartinhoFernandes 仍然与实际问题没有任何关系。但是感谢这个漂亮的标语,以前没听过。 另一方面:C-Cast 不是必需的。 m_cache=(float*)new float[1]; 在任何 C++ 代码中都不是一个好主意。 绝对看起来像一个错误。我会报告的。请注意,颠倒成员的顺序可以解决问题。 【参考方案1】:

有人可能会争辩说这是一个编译器错误 - 但“未定义的行为”本质上意味着“任何事情都会发生”,并且您存储对即将被破坏的对象的引用符合未定义的行为。因此,您应该接受这一点并停止使用临时对象初始化引用。

【讨论】:

据我所知,将临时对象绑定到引用是不是未定义的行为(参见 12.2/5 项目符号 1)。但是,使用悬空引用(= 使用生命周期已结束的对象)。 @DyP 我也是这么想的。由于我从未在这种状态下使用过引用,因此我的直觉是该示例应该定义明确。

以上是关于在构造函数中调用的成员变量的析构函数 - 编译器错误?的主要内容,如果未能解决你的问题,请参考以下文章

一个完整的C++类应该包含什么?

类的构造函数/类的析构函数

带有向量的类构造函数中的析构函数调用

虚析构函数

静态对象成员会在所属类的析构函数被调用时自动析构吗?

子类的构造函数,子类的析构函数,子类型关系