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

C++中能否用临时对象指针初始化多态C数组?

在C++中能否用临时对象的指针/引用初始化多态C数组?

问题

在C++中,是否可以使用临时对象的引用/指针来初始化多态C数组(例如类型为ParentClass*的数组,包含Subclass1*、Subclass2*等类型的指针)?由于所有临时对象在编译时都是已知的,我不想使用unique_ptr或任何涉及动态分配的方式(比如用new)。


用例

当前版本

现有代码如下:

class Command {
 public:
  virtual void run(const vector<string>& args) = 0;
};

class Generate: public Command {
 public:
  void run(const vector<string>& args) override;
};

class Scan: public Command {
 public:
  void run(const vector<string>& args) override;
};

我希望定义一个包含static Command* commands[]数据成员的App类,示例如下:

// App.hpp
class App {
  static Command* commands[];
};

// App.cpp
Generate generate;
Scan scan;
Command* App::commands[] {&generate, &scan};

编译错误

在尝试寻找可行方案时,遇到以下编译错误:

taking the address of a temporary object of type 'Generate':
  Command* App::commands[] {&Generate()};
non-const lvalue reference to type 'Generate' cannot bind to a temporary of type 'Generate'
  Command* App::commands[] {&static_cast<Generate&>(Generate())};
cannot initialize an array element of type 'Command *' with an rvalue of type 'const Generate *'
  Command* App::commands[] {&static_cast<const Generate&>(Generate())};

编译器版本:g++: Apple clang version 15.0.0 (clang-1500.0.29.1)

期望改动

我希望在App.cpp中移除全局变量generate和scan,改用临时对象,例如:

// App.cpp
Command* App::commands[] {&Generate(), &Scan()};

更理想的是移除App.cpp,直接在App.hpp中定义commands,例如:

// App.hpp
class App {
  static Command* commands[] {&Generate(), &Scan()};
};

// App.cpp 被删除

解决方案

直接取临时对象的指针是未定义行为,C++标准明确禁止这种操作——临时对象的生命周期通常只持续到所在完整表达式结束,一旦表达式执行完毕,对象就会被销毁,留下的指针会变成悬空指针,后续访问会导致程序崩溃或不可预测的行为。

不过你可以通过以下几种合法方式实现需求,既避免全局变量,又无需动态分配:

  1. 静态局部对象封装
    在数组初始化时调用返回对象引用的函数,函数内部用静态局部对象保证生命周期:

    // App.hpp
    class App {
      static Command& getGenerate() {
        static Generate instance;
        return instance;
      }
      static Command& getScan() {
        static Scan instance;
        return instance;
      }
      static Command* commands[] {&getGenerate(), &getScan()};
    };
    

    静态局部对象的生命周期从第一次调用函数开始,直到程序结束,完全符合需求,且可直接在头文件中定义,无需cpp文件。

  2. inline静态成员初始化(C++17及以上)
    利用C++17的inline静态成员特性,直接在类内部初始化包含静态对象地址的数组:

    // App.hpp
    class App {
      inline static Generate generate;
      inline static Scan scan;
      inline static Command* commands[] {&generate, &scan};
    };
    

    这种方式不需要单独的cpp文件,inline关键字确保静态成员在整个程序中只有一个实例,避免多重定义问题。

  3. constexpr静态对象(若类构造函数支持)
    如果Generate和Scan的构造函数是constexpr,可以用编译期静态对象初始化数组:

    // App.hpp
    class Command {
     public:
      constexpr Command() = default;
      virtual void run(const vector<string>& args) = 0;
    };
    
    class Generate: public Command {
     public:
      constexpr Generate() = default;
      void run(const vector<string>& args) override;
    };
    
    class Scan: public Command {
     public:
      constexpr Scan() = default;
      void run(const vector<string>& args) override;
    };
    
    class App {
      inline static constexpr Generate generate{};
      inline static constexpr Scan scan{};
      inline static Command* commands[] {&generate, &scan};
    };
    

    该方式无需cpp文件,对象在编译时完成初始化,契合你“编译时已知”的要求。


关键原因

数组是静态存储期对象,生命周期贯穿整个程序,但临时对象的生命周期最多到所在表达式结束。两者生命周期不匹配,即使通过强制转换获取临时对象地址,后续访问时对象已销毁,属于未定义行为,因此编译器直接禁止此类操作。

内容的提问来源于stack exchange,提问作者T. R. Bernstein

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:05:30