Перегрузка нового оператора - PullRequest
0 голосов
/ 19 июля 2011

Можно ли перегрузить оператор new, чтобы объект не создавался, а вместо этого возвращал существующий объект.

Если это возможно, как вы могли бы создать объекты в первую очередь?: D

Это звучит странно, я знаю.Я пытаюсь скрыть некоторые детали от клиента.Я делаю игру для PS2, я хотел бы иметь синтаксис New Foo(), но хочу получить список готовых объектов, которые можно использовать вместо этого.

Я не вижу возможности обойти это как новый операторвозвращает указатель на доступную память.

new Foo;

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

};

Ответы [ 8 ]

6 голосов
/ 19 июля 2011

Насколько я знаю, вы не можете изменить этот аспект new , чтобы он фактически создавал объект.Перегруженный оператор просто возвращает необработанную память, а затем язык автоматически создает объект в этой памяти.Вы не контролируете этот шаг.

В любом случае, если вы не получите новый объект, в чем смысл этого синтаксиса?

4 голосов
/ 19 июля 2011

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

Должен рассказать вам все, что вам нужно знать!

или, может быть, даже этот

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

оператор new являетсяглобальная функция, которую вы можете переопределить.

Не забудьте, что вам также нужно предоставить оператор удаления, если вы предоставляете новый оператор.

Я предполагаю, что вы пытаетесь настроить пул памяти, не забудьте на самом деле измеритьпроизводительность с и без, так как это не всегда стоит хлопот ИМХО.

Редактировать: Читая между строк вашего вопроса и некоторые другие ответы, я думаю, вы действительно хотите оставитьперегрузка нового / удаляемого и одиночного шаблона в одиночку.Вместо этого перейдите на фабричный подход.

Весь ваш код вызывает функцию

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 
 }
2 голосов
/ 19 июля 2011

Вы можете перегрузить 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;
    }
}
1 голос
/ 19 июля 2011

Перегрузка new чревата опасностью .Это больше, чем просто 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;
}

Множество возможностей.

1 голос
/ 19 июля 2011

Если вы хотите отдельный объект, можно использовать шаблон Singleton .Если вам нужно несколько объектов, вам нужен пул объектов .

0 голосов
/ 23 июля 2017

Особенность использования operator :: 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++];
    }
}
0 голосов
/ 19 июля 2011

Я бы предложил использовать шаблон моста .

Это означает, что ваш клиент может вызывать new как обычно для тривиального открытого класса, который внутренне делает выбор о том, что создавать или не создавать.

Содержащийся класс может быть закрытым для вашей библиотеки.

0 голосов
/ 19 июля 2011

Мало того, что вы не можете изменить operator new, чтобы делать то, что вы хотите (это только функция распределения), вы не должны.Подрыв значения нового выражения усложнит понимание вашего кода, без каких-либо преимуществ .

Если вам нужна новая функциональность, напишите код!Здесь вам может понадобиться класс (мы не можем сказать) или просто функция:

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

Затем клиентский код использует эту фабричную функцию для получения нужных ему объектов.Что на самом деле делает функция ?Кеширует ли он объекты с помощью шаблона Flyweight?Кеширует ли он память вместо объектов?Код клиента не волнует и не хочет .Все, что его волнует, - это то, что он получает объект, который запрашивает.

Между тем, код в функции делает все, что ему нужно.Если завтра вы поймете, что ошиблись, вы можете изменить код внутри, и, надеюсь, это не повлияет на все остальное.По крайней мере, у него есть шанс на победу.

...