2015-04-04 88 views
3

我正在尝试使用C++进行使用gcc-avr的AVR编程。主要问题是没有libC++可用,并且实现没有定义任何新的或删除操作符。同样,没有包含使用展示位置的标题新不是一个选项。在C++中绕过构造函数

当试图分配我很想只是做这个新的动态对象:

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

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

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


展开上述问题。

使用标准的g ++和placement new,假设C++使用与C一样简单的内存匹配方式(下面的代码示例),可以以两种方式颠覆构造函数。

  1. 使用placement new来初始化任何分配的内存。
  2. 直接使用类方法初始化分配的内存。

当然,这仅成立当假设为真:

  • 对象的内存布局是固定在编译时。
  • 内存分配只涉及类变量和观察者正常的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 
+1

我的建议是查看新的实现(例如在libC++中)并将其复制到项目中。 – MikeMB 2015-04-04 20:53:20

+0

'malloc'分配的空间可能无法正确初始化。所以'p-> Init()'可能会导致错误。 – wowofbob 2015-04-04 20:54:43

+0

如果你想要一个C++的方式,你应该跳过'malloc'并且执行'Class * p = :: operator new(sizeof(Class));'。我很确定这不会调用构造函数。但我认为'delete'总是会调用析构函数。 – 2015-04-04 21:02:05

回答

2

如果涉嫌C++编译器不支持operator new,你应该能够简单地提供自己的,无论是在课堂上或作为一个全球性的定义。这里有一个简单的从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++语言有详细的规则来确保在构造函数中发生这种情况时有用和可预测的东西;用普通函数复制它可能会变得棘手,而且速度很快。

+0

很好的解释。我只是补充说,除了新增和删除之外,还有更多avr-g ++没有做的事情。这些包括任何形式的异常和整个libC++库(类,函数,模板等)。总结如下:http://www.atmel.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_cplusplus.html – Kenneth 2015-04-06 11:43:41

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; 

任何类的大小末尾使用可变长度数组/结构完全是一个编译时间常数。