重载new操作符

Overloading the new operator

本文关键字:操作符 new 重载      更新时间:2023-10-16

是否有可能重载new操作符,从而不创建对象,而是返回一个已存在的对象?

如果这是可能的,你怎么能首先创建对象:D

我知道这听起来很奇怪。我想对客户隐瞒一些细节。我正在PS2上制作一款游戏,我想拥有New Foo()语法,但想要一个可以使用的预制对象列表。

我不认为可以规避这个问题,因为new操作符返回一个指向可用内存的指针。

new Foo;
struct Foo
{
    void* operator new(std::size_t)
    {
        // return pre made obj.
    }
};

据我所知,你不能改变new的这一方面,它实际上构造了对象。重载操作符只返回原始内存,然后语言在该内存中自动构造对象。你不能控制这一步。

无论如何,如果你没有得到一个new对象,那么语法的意义是什么呢?

http://www.cplusplus.com/reference/std/new/operator%20new%5B%5D/

应该告诉你所有你需要知道的!

或者这个

http://www.cplusplus.com/reference/std/new/operator%20new/

operator new是一个可以覆盖的全局函数。

不要忘记,如果你提供了operator new,你还需要提供operator delete。

我猜你正在尝试设置一个内存池,记住实际测量性能有和没有,因为它并不总是值得的麻烦。

编辑:

从你的问题和其他一些答案的字里行间看出来,我猜你真的想让重载new/delete和单例模式单独存在。相反,应该采用工厂模式方法。

你所有的代码都调用

SomethingStatic: GetNewWhatsit ()

函数(而不是构造对象)它返回数组中的下一个指针。指向对象的指针数组,这些对象是在程序初始化时以常规方式创建的。

 whatsit* GetNewWhatsit()
 {
  if (num_of_objects > ARRAY_SIZE(whatsit_cache))
   return 0;
  else 
   return whatsit_cache[num_of_objects++];  // post inc. on purpose 
 }

您可以重载operator new,但是重载的操作符不返回对象。它返回对象的内存,并安排调用构造函数。

所以你不能做你想做的事。

相反,如果您试图避免的成本是内存分配的成本,那么您的重载可以从预分配的块中分配一些内存。显然,你要负责跟踪哪些是免费的,哪些不是,你的挑战是比PS2开发工具包附带的分配器更有效地做到这一点。这可能并不太难——如果你只处理一个类,并假设没有人从它派生,那么你就有一个不公平的优势,即分配的大小是固定的。

如果您试图避免调用构造函数的成本,那么operator new对您没有帮助,但是您可以编写一种包装器:

struct FooWrapper {
    Foo *foo;
    FooWrapper(): foo(choose_a_pre_existing_foo()) { }
    ~FooWrapper() {
        foo->reset(); // clear up anything that shouldn't be kept
        return_to_the_pool_for_reuse(foo);
    }
  private:
    FooWrapper(const FooWrapper &);
    FooWrapper &operator=(const FooWrapper &);
};
Foo *choose_a_pre_existing_foo() {
    // possibly some kind of synchronization needed if list is global
    // and program is multi-threaded.
    if list_of_foos.empty() {
        return new Foo();
    } else {
        Foo *f = list_of_foos.back();
        list_of_foos.pop_back();
        return f;
    }
}

超载new充满危险。它不仅仅是c++的malloc,它在对象生命周期和异常安全等方面具有重要的语义。

当你调用new时,构造函数被调用。你不会想要构造一个对象两次,因为你不可能销毁它两次。你最多只能泄漏资源。

您可能想要的不仅仅是单例,也许可以尝试这样做:

foo。

struct Foo {
    static Foo instance_a;
    static Foo instance_b;
    enum Predefined {
        ALICE,
        BOB
    };
    static Foo & instance (Predefined);
    // ...
}

foo.cpp

Foo Foo :: instance_a (1, 2, 3);
Foo Foo :: instance_b ("alpha");
namespace {
    Foo alice;
    Foo bob (1, "x");
}
Foo & Foo :: instance (Predefined name) {
    // ...
    return alice;
}

很多可能性。

如果你想要单个对象,可以使用Singleton模式。如果需要多个对象,则需要一个对象池。

您不仅不能修改operator new来做您想做的事情(它只是一个分配函数),而且不应该这样做。破坏新表达式的含义会使代码更难理解,没有任何好处

如果你想要全新的功能,那就写代码吧!在这里,您需要的可能是一个类(我们无法判断),或者可能只是一个函数:

template<typename T, typename... U>
std::shared_ptr<T> // for one std::shared_ptr seems appropriate
make(U&&... u);
客户端代码然后使用这个类似工厂的函数来获取它需要的对象。函数实际上是做什么的?它是否使用Flyweight模式缓存对象?它缓存内存而不是对象吗?客户端代码不在乎,也不想。它所关心的只是获取它所请求的对象。

同时,函数中的代码做它需要做的任何事情。如果明天你意识到你做错了,你可以修改里面的代码,希望它不会影响到其他部分。至少它还有奋斗的机会。

我建议使用桥接模式。

这意味着你的客户端可以在一个普通的公共类上调用new,这个公共类内部会选择构造什么或不构造什么。

包含的类可以是库的私有类

使用operator::new的问题是编译器会在调用new之后自动生成一个操作码来调用构造函数,因此new本身并不负责构造函数。你想要的是一个单例:

struct Foo
{
    static Foo& NewObject()
    {
        static Foo* _foo = new Foo;
        return *_foo;
    }
}

或某种数组:

struct Foo
{
    static Foo& NewObject()
    {
        static Foo* _foo = new Foo[128];
        static int index = 0;
        if(index == 128)
            index = 0;
        return _foo[index++];
    }
}