简体   繁体   English

就地重新初始化对象

[英]In place reinitialization of objects

I'm working on an embedded system, where memory is scarce, and more importantly, since there's a soft realtime constraint (ie it's a bug if we don't make the deadline, but no one dies), I cannot use dynamic memory allocation. 我正在开发一个内存稀缺的嵌入式系统,更重要的是,因为有一个软实时约束(即如果我们没有截止日期,但没有人死,这是一个错误),我不能使用动态内存分配。

However, there's the occasional need to re-initialize a subsystem, and there's been a number of bugs related to not-quite-everything getting either cleaned up or reset correctly. 但是,偶尔需要重新初始化一个子系统,并且存在许多错误,这些错误与未完全清除或正确重置的所有内容有关。 This is of course exactly the problem that constructors and destructors are supposed to solve, but since we don't allocate dynamically, we can't use the idiom where we'd destruct the object and then allocate a new one from scratch (mostly the objects of relevance here are globals). 这当然是构造函数和析构函数应该解决的问题,但由于我们不动态分配,我们不能使用我们破坏对象的惯用语,然后从头开始分配一个新的(大多数是这里相关的对象是全局的)。

So in the end there's usually setup code in a constructor, and a reinitialize -type function, which resemble each other but are not identical, since the reinitialize function also does a lot of what a destructor would do. 因此,最终通常在构造函数中设置代码,并且重新初始化类型函数彼此相似但不相同,因为重新初始化函数也执行了很多析构函数的操作。

One way out that I'm thinking of is to write a "renew" -template (this is just a draft, may contain erors and possibly not complet): 我想到的一个方法是写一个“更新” - 模板(这只是一个草稿,可能包含错误,可能不包括complet):

template<typename T, typename .. Args>
void renew(T & obj, Args&&... args) {
  obj.~T();
  new(&obj) T(std::forward<Args>(args)...); 
}

which could be used to reinitialize non-dynamically alloctated variables. 可用于重新初始化非动态分配的变量。 So for example 所以举个例子

A a{17};
... //Do something with a
renew(a, 14);
...//work with the new a, no need to reallocate memory

This would allow getting some of the advantages of constructors and destructors (primarily single way to initialize and deinitialize an object) without dynamic memory allocation. 这将允许获得构造函数和析构函数的一些优点(主要是初始化和取消初始化对象的单一方法),而无需动态内存分配。 Note that the usage above is simplified, in practice this would mostly be used in very specific points in the main loop and on global objects representing the actual physical subsystems. 请注意,上面的用法是简化的,实际上这主要用于主循环中的非常特定的点以及表示实际物理子系统的全局对象。

Question: is this a sensible way of going about it? 问题:这是一种明智的方式吗? Or is there a better alternative? 还是有更好的选择吗?

There's a very similar question here Calling a constructor to re-initialize object . 这里有一个非常相似的问题调用构造函数来重新初始化对象 I'm asking much the same thing but specifically in context of embedded programming, where I can't do things the usual way with dynamic allocation. 我问的是同样的事情,但特别是在嵌入式编程的上下文中,我不能通过动态分配的常规方式来做事。

Dynamic memory allocation and constructors and destructors are completely unrelated. 动态内存分配和构造函数和析构函数完全不相关。

If you are using new or delete (like in your renew function), you are using dynamic memory allocation. 如果您使用new或delete(如在续订功能中),则使用动态内存分配。

A constructor or destructor does not always mean you are dynamically allocating memory. 构造函数或析构函数并不总是意味着您正在动态分配内存。

The 'renew' function should probably be implemented in the operator= of the class, not as an external function, so: 'renew'函数应该可以在类的operator =中实现,而不是作为外部函数实现,因此:

A a{17}
...
a = 14;
...

Example of what you should do: 你应该做什么的例子:

class TwoInts
{
private:
    int int1;
    int int2;
public:
    TwoInts(int a = 6, int b = 7): int1(a), int2(b) {}
    TwoInts(const TwoInts& other): int1(other.int1), int2(other.int2) {}

    TwoInts& operator=(TwoInts& other)
    {
        a = other.a;
        b = other.b;
    }
};

TwoInts i(16);
//do stuff
i = TwoInts(68, 14);
//do stuff

The above code does not do any memory allocation. 上面的代码没有做任何内存分配。

The only reason I see were you would like to your the construction you propose is where the new class would be of a different derived type. 我看到的唯一原因是你想要你所建议的构造是新类别是不同派生类型的地方。 But in this case you would have to be really be careful to use the exact same amount of memory, not to override following variables. 但在这种情况下,您必须非常小心使用完全相同的内存量,而不是覆盖以下变量。 I know of one example where this is used in mbed . 我知道在mbed中使用它的一个例子。 It used to retype their function callbacks, search for "new" and you will various examples. 它曾经重新输入它们的函数回调,搜索“new”,你会看到各种各样的例子。

I am wondering if you have considered using a "clear" function in your class? 我想知道你是否考虑过在课堂上使用“清晰”功能? In my honest opinion as far as I can see now this would be the easiest solution. 据我所知,就我所知,这将是最简单的解决方案。

An alternative solution would be to write a memory manager which has a fixed buffer size. 另一种解决方案是编写具有固定缓冲区大小的内存管理器。 From this buffer you can then "dynamically" allocate memory. 然后,您可以从此缓冲区“动态”分配内存。 I do not think this is the solution you are looking for as this is more useful for when you often need to allocate and deallocate multiple objects. 我不认为这是您正在寻找的解决方案,因为当您经常需要分配和释放多个对象时,这更有用。

I cannot use dynamic memory allocation 我不能使用动态内存分配

Then the keywords new and delete cannot be allowed to exist in your source. 然后,您的源中不能存在关键字newdelete Also make sure to nuke the .heap segment from your memory map. 还要确保从内存映射中查找.heap段。

Similarly, you don't need/want RAII for any class that's some manner of driver. 同样,对于任何类型的驱动程序,您不需要/想要RAII。 In buzz word terms, any such class must be a "singleton". 在流行语中,任何这样的类必须是“单身”。 Or rather, a class that allows for n number of static instances, where n is the number of hardware devices of that type present. 或者更确切地说,是允许n个静态实例的类,其中n是存在的那种类型的硬件设备的数量。

As for the solution to your problem, it is simply this: 至于你的问题的解决方案,它只是这样:

public:
  void construct() { ... }

  Foo()
  : /* init internal stuff here if needed */
  {
    /* init internal stuff here if needed */    
    construct();
  }

Where the constructor ensures that everything has default values, and construct is written so that it either works on non-initialized internals (null etc), or in case the internals are set, free up resources. 在构造函数确保所有内容都具有默认值的情况下,并且编写construct以使其在非初始化内部(null等)上工作,或者在设置内部的情况下,释放资源。

But there should not even be anything for you to "free up", since this is an embedded system. 但是,由于这是一个嵌入式系统,因此甚至不应该有任何东西可以“释放”。 If you need to gracefully terminate on-going data transmissions, NVM writes etc, that code shouldn't be in the destructor. 如果您需要优雅地终止正在进行的数据传输,NVM写入等,该代码不应该在析构函数中。 Because you shouldn't need a destructor (nor should you need the rest of the rule of three ), since you should only have one instance of the class. 因为你不需要析构函数(也不需要三个规则的其余部分),因为你应该只有一个类的实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM