C++ 在派生类中重写运算符new/delete

C++ 在派生类中重写运算符new/delete,c++,memory-management,singleton,overriding,new-operator,C++,Memory Management,Singleton,Overriding,New Operator,我有一个无状态的抽象基类,各种具体类从中继承。其中一些派生类也是无状态的。因为它们中的许多都是在运行期间创建的,所以我希望通过重写操作符new()/delete(),让所有无状态派生类模拟一个单例来节省内存和开销。简化的示例如下所示: #include <memory> struct Base { virtual ~Base() {} protected: Base() {} // prevent concrete Base objects }; struct D1

我有一个无状态的抽象基类,各种具体类从中继承。其中一些派生类也是无状态的。因为它们中的许多都是在运行期间创建的,所以我希望通过重写操作符new()/delete(),让所有无状态派生类模拟一个单例来节省内存和开销。简化的示例如下所示:

#include <memory>

struct Base {
  virtual ~Base() {}
 protected:
  Base() {}   // prevent concrete Base objects
};

struct D1 : public Base {  // stateful object--default behavior
  int dummy;
};

struct D2 : public Base {  // stateless object--don't allocate memory
  void* operator new(size_t size)
  {
    static D2 d2;
    return &d2;
  }
  void operator delete(void *p) {}
};

int main() {
  Base* p1 = new D1();
  Base* p2 = new D1();
  Base* s1 = new D2();
  Base* s2 = new D2();
  delete p1;
  delete p2;
  delete s1;
  delete s2;
  return 0;
}
#包括
结构基{
虚拟~Base(){}
受保护的:
Base(){}//防止具体的基础对象
};
结构D1:公共基{//有状态对象--默认行为
int假人;
};
结构D2:公共基{//无状态对象--不分配内存
void*运算符新(大小\u t大小)
{
静态D2;
返回&d2;
}
void运算符delete(void*p){}
};
int main(){
基准*p1=新的D1();
Base*p2=新的D1();
Base*s1=新的D2();
Base*s2=新的D2();
删除p1;
删除p2;
删除s1;
删除s2;
返回0;
}
此示例不起作用:
deletes2失败,因为
删除s1称为
~Base()
,它在
d2
中取消分配共享的
Base
。这可以通过向Base添加new/delete重载的相同技巧来解决。但我不确定这是最干净的解决方案,甚至不是正确的解决方案(valgrind没有抱怨,FWIW)。我希望得到建议或批评


编辑:事实上,情况更糟。本例中的基类并不像我所说的那样是抽象的。如果通过添加纯虚方法将其抽象化,那么我就不能再应用new/delete重写技巧,因为我不能有Base类型的静态变量。所以我没有解决这个问题的办法

您不能这样做-这将违反“对象标识”要求,即。您必须为每个对象分配不同的内存块-如果您重写
运算符new
以使用专门为固定大小的对象定制的快速内存块分配器,则可以相当快地完成此操作。

我想说,最好的解决方案是使派生类成为实际的单例。将派生构造函数设为私有,只提供一个static Base*getInstance()方法,该方法创建所需的对象或返回静态实例。这种方法获取D1对象的唯一方法是通过此方法,因为调用新的D1是非法的。

您是否实际测量过这种“技巧”的内存量正在拯救你吗?为什么要对无状态对象使用
new
?@Bo Persson:即使对象是无状态的,它也可以有特定的类型,不同的对象有不同的类型可能很重要。@Martinho Fernandes:你能解释一下为什么没有数据成员的对象不是无状态的吗?@Eitan:重载
new
/
delete
仅处理内存分配/释放。编译器会自动生成相应的构造函数/析构函数调用。因此,如果有像
Base*s1=newd2()这样的语句;Base*s2=新的D2(),分配给D2的内存将构造两次,而不会破坏先前构造的D2对象。除非D2是普通的旧数据,否则这会导致问题。实际上我是从类似的东西开始的。但是,我没有使用getInstance(),而是在每个派生类中使用create()方法(和deallocate()),该方法返回一个指向相应类型对象的指针,该对象在有状态对象中是新的,在无状态对象中是静态的。这很好,但我想知道我是否可以使用语言机制(即new()和delete())使其更干净。使用语言机制并不干净,因为许多人在使用这些机制时会做出某些断言,以及那些断言(例如,new确实分配了一个新对象,并且使用new compare创建的两个对象的地址不相等)。即使方法名
create
也可能会产生这些误解,因此我会选择
get
release
,或者每次用户需要一个新的无状态对象时,就简单地分配一个新的无状态对象,这不应该是一个问题,除非这些对象用于非常关键的性能代码部分。@smerlin:感谢您对用户的深入了解期望值。我认为您的命名方案更好。至于性能,我对其进行了测量,并在早期的评论中发布,这一技巧的效果是将整个应用程序的运行时间减少了约25%,非常显著。感谢链接——这是一个很好的线索。我认为这个问题与我的问题之间的微妙区别在于我确实需要t所有从D2::operator new()返回的对象都是相同的对象。正如我前面提到的,将其实现为单例或单独的create/deallocate方法很好,但我希望以一种不太麻烦的方式来实现(使用new()/delete()可以说是这样)