在C ++中绕过构造函数

时间:2015-04-04 20:49:34

标签: c++ constructor avr

我正在尝试使用gcc-avr将C ++用于AVR编程。主要问题是没有可用的libc ++,并且实现没有定义任何new或delete运算符。同样,没有标题要包含使用展示位置new不是一个选项。

尝试分配新的动态对象时,我很想这样做:

Class* p = reinterpret_cast<Class*>(malloc(sizeof(Class)));
p->Init();

其中Init()手动初始化所​​有内部变量。但这是安全的还是可能的?

我已经读过C ++中的对象构造有点复杂但没有新的或删除如何初始化动态分配的对象?


扩展上述问题。

使用标准的g ++和placement new可以以两种方式破坏构造函数,假设C ++使用与C相同的直接内存方式(下面的代码示例)。

  1. 使用placement new初始化任何已分配的内存。
  2. 使用类方法直接初始化已分配的内存。
  3. 当然,只有在假设为真的情况下,这才会成立:

    • 对象的内存布局在编译时是固定的。
    • 内存分配仅涉及类变量和观察者正常的C规则(按照与内存边界对齐的声明顺序分配)。

    如果以上所述,我可以不仅使用malloc分配内存并使用reinterpret_cast转换为正确的类并手动初始化它?当然这既是非便携式也是黑客攻击,但我能看到的另一种方法是解决问题,而不是使用动态分配的内存。

    示例:

    Class A {
        int i;
        long l;
        public:
            A() : i(1), l(2) {}
            int get_i() { return i; }
            void set_i(int x) { i = x; }
            long get_l() { return l; }
            void set_l(long y) { l = y; }
    };
    
    Class B {
         /* Identical to Class A, except constructor. */
         public B() : i(3), l(4) {}
    };
    
    int main() {
         A* a = (A*) ::operator new(sizeof(A));
         B* b = (B*) ::operator new(sizeof(B));
    
         /* Allocating A using B's constructor. */
         a = (A*) new (a) B();
    
         cout << a->get_i() << endl; // prints 3
         cout << a->get_l() << endl; // prints 4
    
         /* Setting b directly without constructing */
         b->set_i(5);
         b->set_l(6);
    
         cout << b->get_i() << endl; // prints 5
         cout << b->get_l() << endl; // prints 6
    

2 个答案:

答案 0 :(得分:2)

如果您所谓的C ++编译器不支持operator new,您应该能够在类中或作为全局定义提供您自己的C ++编译器。这是一个来自an article discussing operator new的简单版本,略有修改(同样可以在许多其他地方找到):

void* operator new(size_t sz) {
    void* mem = malloc(sz);
    if (mem)
        return mem;
    else
        throw std::bad_alloc();
}


void operator delete(void* ptr) {
    free(ptr);
}

还可以找到operator new的更长时间的讨论,特别是针对特定类的定义,here

从评论中看来,似乎给定了这样的定义,您的编译器就会愉快地支持标准的堆栈上的对象创建:

auto a = std::make_shared<A>();
A *pa = new A{};

使用问题代码片段中显示的Init方法的问题是,使用继承正常工作可能会很麻烦,尤其是多重或虚拟继承,至少在对象期间有什么问题施工可能会抛出。 C ++语言有详细的规则,以确保在构造函数的情况下发生有用和可预测的事情;用普通函数复制可能会变得相当棘手。

答案 1 :(得分:1)

您是否可以使用malloc() - reinterprete_cast<> - init()方法取决于您是否拥有虚拟功能/继承。如果你的课程中没有虚拟的东西(它是一个普通的旧数据类型),你的方法就可以了。

但是,如果其中有虚拟内容,那么您的方法将会失败:在这些情况下,C ++会在您的类的数据布局中添加一个v表,如果不深入到未定义的行为,则无法直接访问。通常在运行构造函数时设置此v表指针。由于您无法安全地模仿构造函数在这方面的行为,因此您必须实际调用构造函数。也就是说,您必须至少使用placement-new。

像Christopher Creutzig所说,提供无类operator new()是提供完整C ++功能的最简单方法。它是由new表达式在内部使用的函数,用于提供可以调用构造函数以提供完全初始化对象的内存。


最后一点保证:只要您不在struct这样的末尾使用可变长度数组

typedef struct foo {
    size_t arraySize;
    int array[];
} foo;

任何类/结构的大小完全是编译时常量。

相关问题