如何强制调用类的全局实例的析构函数和构造函数(因此“重新初始化”该类实例)

时间:2019-08-02 08:27:01

标签: c++ class constructor driver

我有一个用C ++开发的固件项目,其中所有驱动程序都由一个类组成,没有简单的方法对其进行修改。

驱动程序用于uP的内部外围设备,并由该类的全局实例实现;现在,我必须修改该功能,并在出现异常情况或类似情况时允许驱动程序“重新初始化”。

驱动程序的初始化是在驱动程序的构造函数中完成的(以这种方式实现,我无法对其进行修改),并且没有明确的方式(特定方法或类似方法)来调用该功能。因此,我需要强制召回该类的构造函数。 丢失实例的所有信息不是问题,因此可以删除实例并重新制作实例。

例如,部分代码与Mbed库中的代码类似:

class SPI {

public:

    SPI(PinName mosi, PinName miso, PinName sclk, PinName ssel=NC);

    void format(int bits, int mode = 0);
[.....]

  ~SPI()

}

在代码的其他部分中有一个全局实例:

SPI SPI_Master(P0_9, P0_8, P0_7);

void funcA(int b){
}

所以函数中有一种方法可以做类似的事情:

void SPIException(){
   delete SPI_Master;
   SPI_Master = new SPI (P0_9, P0_8, P0_7);
}

那么要强制召回构造函数吗?

和一些澄清:

SPI SPI_Master(P0_9,P0_8,P0_7);

与以下内容完全相同:

SPI SPI_Master =新的SPI(P0_9,P0_8,P0_7);

3 个答案:

答案 0 :(得分:1)

析构函数只是具有特殊名称的函数。像实例一样调用它。〜T()。

void destroy()
{
  SPI_Master.~SPI():
}

您可以通过新放置位置在给定位置强制构造对象。

void reinit()
{
  new(&SPI_Master) SPI(/*arguments go here*/);
}

答案 1 :(得分:0)

详细说明SPD的提示:

  

2)显式调用析构函数,并使用new放置在同一内存地址中构建SPI

我做了一个小样本:

#include <iostream>

struct Global {
  int a1, a2;
  Global(int a1, int a2): a1(a1), a2(a2)
  {
    std::cout << "Global::Global(): a1: " << a1 << " a2: " << a2 << '\n';
  }
  ~Global()
  {
    std::cout << "Global::~Global()\n";
  }
  Global(const Global&) = delete;
  Global& operator=(const Global&) = delete;
};

std::ostream& operator<<(std::ostream &out, const Global &global)
{
  return out
    << "&global: " << &global << '\n'
    << "global: global.a1: " << global.a1 << " global.a2: " << global.a2 << '\n';
}

Global global(123, 456);

int main()
{
  std::cout << "Initially: " << global;
  global.a1 = 321; global.a2 = 654;
  std::cout << "Changed: " << global;
  global.~Global();
  new(&global) Global(123, 456);
  std::cout << "Re-Inited: " << global;
  std::cout << "Exiting...\n";
}

输出:

Global::Global(): a1: 123 a2: 456
Initially: &global: 0x6013d8
global: global.a1: 123 global.a2: 456
Changed: &global: 0x6013d8
global: global.a1: 321 global.a2: 654
Global::~Global()
Global::Global(): a1: 123 a2: 456
Re-Inited: &global: 0x6013d8
global: global.a1: 123 global.a2: 456
Exiting...
Global::~Global()

Live Demo on coliru

请注意:

全局实例有其局限性,但是它可能存在,并且出于任何原因都不能更改。 (对于单身人士,应提及Singleton Pattern,这有助于解决static initialization order ‘fiasco’ (problem)?。)

通常,由new创建的东西通常会构造到分配给堆的内存中(即,如果未使用自定义new),这与在其他地方创建的静态实例相反。 / p>

Placement new可以进行构造而无需分配,即不存储到调用方提供的存储中,即与存储的分配方式无关。

答案 2 :(得分:0)

SPI SPI_Master(P0_9, P0_8, P0_7); 与...不同 SPI SPI_Master = new SPI(P0_9, P0_8, P0_7);

最后一行将不编译,只要new返回指向堆对象(而非对象)中的指针。同时SPI SPI_Master(P0_9, P0_8, P0_7);位于堆栈中。您可以阅读有关内存类型的其他信息。 正确的格式为: SPI * SPI_Master = new SPI(P0_9, P0_8, P0_7); 是的,这是您可以使用的解决方案之一。

以及您提到的函数之后的某处将按预期工作:

void SPIException(){
   delete SPI_Master;
   SPI_Master = new SPI (P0_9, P0_8, P0_7);
}

但是它将调用使用SPI_Master的所有代码中的更改:从SPI_Master.anyCall()SPI_Master->anyCall()

如果您无法更改行: SPI SPI_Master(P0_9, P0_8, P0_7);

您可以尝试显式调用析构函数,然后覆盖相同的变量:

SPI_Master.~SPI(); 

SPI_Master = SPI(P0_9, P0_8, P0_7);

但是要小心: 1)这取决于构造函数和析构函数的实际作用。 2)销毁期间的销毁和建造订单,将产生您的编译器:  -创建新的,删除旧的并分配新的  -删除旧的,创建新的,分配新的

因此,此解决方案可能非常危险。

相关问题