【问题标题】:Can I implement the Factory Method pattern in C++ without using new?我可以在不使用 new 的情况下在 C++ 中实现工厂方法模式吗?
【发布时间】:2026-02-06 23:20:19
【问题描述】:

我在嵌入式环境 (Arduino/AVR ATMega328) 中工作,并希望在 C++ 中实现工厂方法模式。但是,我使用的编译器 (avr-gcc) 不支持 new 关键字。有没有办法在不使用new 的情况下实现这种模式?

【问题讨论】:

  • 如何分配内存? D:
  • 除此之外,它是否支持放置新的?我能想到的最好的方法是静态分配一块内存,然后你可以在那里放置新的任何东西。
  • 内存要么分配在堆栈上(包括基于堆栈的对象构造),要么使用 malloc() - 它不支持对象构造。也不支持新的展示位置...
  • 是的(见我之前的评论)。
  • 我想一定有某种方法可以就地调用构造函数。它可能不符合标准,但它应该存在,以便您拥有相当于新的展示位置。

标签: c++ embedded avr avr-gcc factory-method


【解决方案1】:

由于AVR编译器是基于gcc编译器的,所以很有可能支持new关键字。你得到的错误到底是什么。我猜这是一个未定义函数的链接/编译器错误,即 operator new。 new操作符和new操作符是有区别的,前者用于创建对象,后者用于为对象分配内存。 new 运算符为正在创建的对象类型调用 operator new,然后初始化对象的 v-table 并调用对象的构造函数。 Reading this FAQ 表示标准库中未定义 operator new。这很容易解决,只需定义一个:

void *operator new (size_t size)
{
  return some allocated memory big enough to hold size bytes
}

你还需要定义一个删除:

void operator delete (void *memory)
{
   free the memory
}

唯一要添加的是内存管理、内存块的分配和释放。这可以很简单地完成,注意不要破坏任何现有的分配内存(代码、静态/全局数据、堆栈)。您应该定义两个符号 - 一个用于空闲内存的开始,一个用于空闲内存的结束。您可以动态分配和释放该区域中的任何内存块。您需要自己管理此内存。

【讨论】:

    【解决方案2】:

    工厂方法的大图是对象创建,这意味着堆内存消耗。在嵌入式系统上,您受到 RAM 的限制,需要在做出所有设计决策时考虑到内存限制。 ATmega328 只有 2 KB RAM。我建议不要在如此狭小的空间内使用动态分配的内存。

    如果不更详细地了解您的问题,我建议静态声明该类的少数实例并以某种方式重用这些实例。这意味着您需要知道创建对象的时间和原因以及——同样重要的是——它们何时以及为什么结束;那么您需要弄清楚一次需要激活多少个以及一次可以激活多少个。

    !!院长

    【讨论】:

    • 所有因为它有 2KB 或 RAM 并不意味着你不能进行动态内存分配。你不能分配那么多。您可以在其中获得 500 个两字节对象(500*(2 用于对象+2 用于分配信息)= 2000)。
    • Skizz,你的前两句话是正确的,但第三句话是不合理的。 2 KB RAM(2048 字节)同时保存 C 堆栈和堆;所以他的 C 堆栈只有 48 个字节(不合理)。需要工厂方法的程序不是一个简单的程序,因此他的 C 调用堆栈的深度会有所不同,并且可能会覆盖堆中的对象。动态内存分配是可能的,但在如此小的内存池中并不安全。静态内存分配将更容易管理,并且更有可能产生正确运行的程序。
    【解决方案3】:

    我在具有严格编码标准(不允许使用“new”或“delete”)的嵌入式系统中解决此问题的一种方法是创建所需对象的静态数组。然后使用指向已分配对象的静态指针,存储这些返回值(使用静态变量和/或成员变量)以供以后执行各种对象。

    // Class File ---------------------------------------------------
    class MyObject {
        public:
            MyObject* getObject();
    
        private:
            const int MAX_POSSIBLE_COUNT_OF_OBJECTS = 10;
            static MyObject allocatedObjects[MAX_POSSIBLE_COUNT_OF_OBJECTS];
    
            static allocatedObjectIndex = 0;
    };
    
    // Implementation File ------------------------------------------
    
    // Instantiate a static array of your objects.
    static MyObject::allocatedObject[MAX_POSSIBLE_COUNT_OF_OBJECTS];
    
    // Your method to return already created objects.
    MyObject* MyObject::getObject() {
    
        if (allocatedObjectIndex < (MAX_POSSIBLE_COUNT_OF_OBJECTS - 1)) {
            return allocatedObjects[allocatedObjectIndex++];
        } else {
            // Log error if possible
            return NULL;
        }
    }
    

    请注意。这全凭记忆,因为我已经超过 8 个月没有写过任何 C++ 了。

    另请注意:这有一个严重的缺点,即您在编译时分配了大量 RAM。

    【讨论】:

    • 您可以定义一个 MyObject::operator new (size_t) 来完成上述所有操作,即使用预先分配的数组,同时保持熟悉的新 MyObject (args) 语法而不是非标准的语法.这样,您就可以正确地构造和销毁对象。
    • 在嵌入式系统中,我认为在编译时分配一堆 RAN 是好事,而不是缺点
    【解决方案4】:

    这样的事情呢?

    MyClass *objp = (MyClass*)malloc(sizeof(MyClass));
    *objp = MyClass();  // or any other c'tor
    

    编辑:忘了说,它假设 MyClass 有一个赋值运算符。

    EDIT2:我忘记的另一件事 - 是的,有一个问题(它是 C++,总是有问题)。您必须为对象手动调用 d'tor,因为您不能免费使用。

    【讨论】:

    • 我妈妈总是警告我不要使用 malloc() 来实例化 C++ 对象。即使您正在调用构造函数并将其数据复制到分配的内存中,这种方法有什么问题吗?
    • 是的,有明显的陷阱:内存块未初始化,因此赋值运算符应该做出相应的反应——如果它看到一个“初始化”指针成员,它不应该尝试释放它,因为它包含垃圾和悬空。我想最好只调用 calloc() 来获得零初始化的内存块以避免这种情况。
    • 好吧,我将清理它并测试分配是否成功到生产代码。据我所知,也许他们想从某个内存池而不是 malloc 分配内存...
    • 这行不通。与上面的注释相反,第二行没有在 *objp 上调用任何构造函数,它只是在 UNINITIALIZED 对象上调用赋值运算符。如果 MyClass 有任何虚方法或虚基,它迟早会因为虚表指针未初始化而对你产生影响。
    【解决方案5】:

    你会做malloc吗?如果是这样,您可以这样 malloc 对象。

    另外,您想从工厂创建的对象的性质是什么?

    • 它们是不可变的吗?
    • 工厂是否只打算生成有限的一组可在编译时知道的对象?

    如果两个问题的答案都是肯定的,您可以为您的一组不可变对象静态分配内存,并让工厂方法返回指向相应对象的指针。

    如果任何一个问题的答案都是否定的,这将不起作用。同样使用这种方法,您会遇到总是分配内存的问题。

    【讨论】:

      【解决方案6】:

      如果你使用工厂意味着你想要一些动态绑定行为,这表明你有一些虚函数。虽然可以使用 malloc() 为对象分配内存,但类的 vtable 将无法正确设置,因此对虚函数的调用将崩溃。当需要动态绑定时,我看不到任何方法。

      【讨论】:

      • 这可以解决 - 在堆栈上分配一个对象并调用 memcpy 以获得 vtable 指针以将其复制到新分配的堆对象上。
      • 那么你是说 Tal 的回答 (*.com/questions/1031301/…) 行不通吗?
      • 我没试过。但在我看来,这行不通。但是,sharptooth 建议的解决方法可能有效。
      【解决方案7】:

      如果没有办法在运行时实例化一个类,我想这是不可能的。您所能做的就是在编译时预先分配一些对象,创建对它们的引用并在需要时返回它们。

      【讨论】:

        最近更新 更多