You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

构造函数传递引用致运行时异常,继承测试夹具崩溃排查

问题:单元测试夹具继承被测类时构造函数崩溃,组合方式却正常

重构代码后,单元测试出现崩溃:被测代码在项目中运行正常,但在测试夹具中继承该类时,构造函数执行报错。

原始代码

class ClassA : public InterfaceA
{
public:
   virtual void SetMember(float member_in) override
   {
      member = member_in;
   }

private:
   float member;
};


class ClassB
{
public:
   ClassB(InterfaceA& a_in)
   {
      a_in.SetMember(6.28f);
   }
};


class ClassBTestFixture : public ClassB
{
public:
   ClassBTestFixture() : ClassB(a1) { }

private:
   ClassA a1;
};

class ClassBTestFixture2
{
public:
   ClassBTestFixture2() : b(a1) { }

private:
   ClassA a1;
   ClassB b;
};

int main()
{
   ClassBTestFixture fixture;
}

运行时异常信息

this->a_in. was 0xCCCCCCCC

但实例化采用组合而非继承的ClassBTestFixture2时却能正常运行,不过这会提升单元测试的编写难度。该问题在VS2019和Linux GCC环境下表现一致。


重构后的代码(已正常运行)

class ClassA
{
public:
   virtual void SetMember(float member_in)
   {
      member = member_in;
   }

private:
   float member;
};


class ClassB
{
public:
   ClassB(ClassA& a_in) : a(a_in)
   {
   }

   void Init()
   {
      a.SetMember(6.28f);
   }
private:
   ClassA& a;
};

class ClassBTestFixture : public ClassB
{
public:
   ClassBTestFixture() : ClassB(a1)
   {
      Init();
   }

private:
   ClassA a1;
};


int main()
{
   ClassBTestFixture fixture;
}

移除了接口(确认并非问题根源),但仍在向基类构造函数传递成员变量,请问这种写法是否合适?


解答

第一个问题的原因:构造函数初始化顺序规则

C++中基类构造函数的执行优先级高于派生类的成员变量初始化:

  • 当创建ClassBTestFixture对象时,会先调用基类ClassB的构造函数,此时派生类的成员a1还未完成构造,传递给ClassB构造函数的a1引用指向的是未初始化的内存(VS中用0xCCCCCCCC标记未初始化的栈内存),调用a_in.SetMember()时自然会触发运行时异常。
  • 而ClassBTestFixture2采用组合方式时,成员变量的初始化顺序严格按照声明顺序执行:a1先于b初始化,所以b的构造函数调用时,a1已经是有效对象,引用合法,因此能正常运行。

重构后写法的合理性分析

你现在的写法是安全且合适的,但需要注意前提条件:

  1. 基类ClassB的构造函数仅保存引用,没有立即使用该引用调用成员函数——因为此时a1还未构造。
  2. 调用Init()的时机是在派生类的构造函数体中,此时派生类的成员a1已经完成初始化,引用指向的是合法对象,调用a.SetMember()不会有问题。

不过也要留意后续维护的风险:

  • 绝对不能在基类ClassB的构造函数或析构函数中调用Init(),否则会再次触发未初始化对象的访问问题。
  • 如果后续修改基类代码时,不小心在构造函数中使用了保存的引用,会重新引入崩溃问题。如果要彻底规避这类风险,也可以考虑在测试时外部传入已初始化的ClassA对象,比如:
    class ClassBTestFixture : public ClassB
    {
    public:
       ClassBTestFixture() : ClassB(a1)
       {
          Init();
       }
    
    private:
       ClassA a1;
    };
    
    但就当前的实现而言,只要遵循“不在基类构造/析构中使用引用”的规则,现有写法完全可以正常工作。

内容的提问来源于stack exchange,提问作者agmessier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:34:51