【问题标题】:Overloading the new operator重载新的操作符
【发布时间】:2011-07-19 14:57:29
【问题描述】:

是否可以重载new 运算符,以便不创建对象,而是返回现有对象。

如果可能的话,您如何首先创建对象:D

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

我不认为要规避这一点,因为 new 运算符返回一个指向可用内存的指针。

new Foo;

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

};

【问题讨论】:

    标签: c++ operators overloading


    【解决方案1】:

    据我所知,您无法更改 new 的这一方面,即它实际上构造了对象。重载的操作符只是返回原始内存,然后语言在这个内存中自动构造对象。你无法控制那一步。

    无论如何,如果您没有获得 new 对象,那么该语法的意义何在?

    【讨论】:

    • 我想对客户隐瞒事实。而不是有一种特殊的方式来获取预先制作的对象。这样new PhysicsObject 就会给人一种新的外观,即使它实际上可能已经存在。
    • @PhilCK:这不仅仅是“奇怪”;这是不对的。不要试图这样欺骗你的客户。
    • @PhilCK:您无法获得预制对象。 New 将始终调用构造函数。您可以让用于制作新对象的内存来自预分配的池。
    • @PhilCK,我认为意外后果的可能性使这个想法成为避免的事情。这将如何影响其他库等?
    • “那个语法有什么意义?” HTTP连接池?不,我也不确定这是否是最好的语法,但这是一个示例,其中一个对用户而言是“新”的对象可能拥有来自“旧”的有价值的预先存在的资源" 同一类的实例。
    【解决方案2】:

    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。

    我猜你正在尝试建立一个内存池,记得在有无的情况下实际测量性能,因为它并不总是值得麻烦恕我直言。

    编辑: 在您的问题和其他一些答案之间阅读,我的猜测是您真的想单独留下重载的新/删除和单例模式。而是采用工厂模式方法。

    你所有的代码都调用一个

    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 
     }
    

    【讨论】:

    • 可以这样做,但是here be dragons!另外,你真的不应该,因为它违反了new/delete的正常含义
    • 如果他要一把枪来打开一罐豆子,你会给他一把吗?
    • @Chris Huang-Leaver 是的,涉及内存池。部分问题在于 PS2 在运行时分配速度很慢,需要一些帮助。我正在制作一个 2D 物理引擎,我希望它尽可能用户友好。
    【解决方案3】:

    您可以重载operator new,但重载的运算符不会返回一个对象。它返回一个对象的内存,并安排调用构造函数。

    所以你不能完全按照你的意愿去做。

    相反,如果您要避免的成本是内存分配,那么您的重载可以从预先分配的块中分配一些内存。显然,您随后负责跟踪哪些是免费的,哪些不是,您的挑战是比 PS2 devkit 附带的分配器更有效地执行此操作。这可能并不太难 - 如果您只处理一个类,并假设没有人从它派生,那么分配的大小是固定的,那么您将获得不公平的优势。

    如果您要避免的成本是调用构造函数,那么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;
        }
    }
    

    【讨论】:

    • 这看起来像票。谢谢史蒂夫。
    【解决方案4】:

    重载newfraught with peril。它不仅仅是 C++ 的malloc,它对于对象生命周期和异常安全等具有重要的语义。

    当你调用new 时,构造函数被调用。你不想构造一个对象两次,因为你不能明智地破坏它两次。充其量你会泄漏资源。

    您可能想要的不仅仅是一个单身人士,也许可以尝试这样的事情:

    foo.h

    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;
    }
    

    多种可能性。

    【讨论】:

    • 但是重载 operator newnew 表达式的构造部分无关。
    • 你可以在不调用new的情况下进行构造,它被称为“placement new”,但你不能在没有构造函数的情况下调用operator new
    • 相反,您可以随时致电operator new(size)(或其他形式,或全班范围的形式),尝试一下(但这与手头的内容无关)。跨度>
    【解决方案5】:

    如果您想要单个对象,可以使用Singleton pattern。 如果你想要多个对象,你需要一个object pool

    【讨论】:

    • 不,我不是在追求一个实例,而是在一个列表中有很多实例,它们是在游戏开始之前创建的,而 new 只会将一个预先存在的实例从列表中拉出。
    【解决方案6】:

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

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

    template<typename T, typename... U>
    std::shared_ptr<T> // for one std::shared_ptr seems appropriate
    make(U&&... u);
    

    然后客户端代码使用这个类似工厂的函数来获取它需要的对象。函数实际上做了什么?它是否使用享元模式缓存对象?它是否缓存 memory 而不是对象?客户端代码不关心并且不想。它所关心的只是它获得了它所要求的对象。

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

    【讨论】:

      【解决方案7】:

      我建议使用bridge pattern

      这意味着您的客户可以在内部选择构造或不构造什么的普通公共类上正常调用 new。

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

      【讨论】:

        【解决方案8】:

        使用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++];
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-10
          • 2020-08-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-04
          相关资源
          最近更新 更多