Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Не правильно работает перегрузка new.


Автор: Chaos A.D. 14.5.2005, 13:08
У меня несколько классов. Ну, скажем, абстрактный Foo в файле Foo.h; Bar и Banana, наследующие гомоморфный интерфейс у Foo. Они лежат в Foo.cpp. Их создает производящая функция. Также есть мой пул памяти. Примерный инрефейс можно обрисовать так:

Код

class Pool
{
    char *PoolData;
    public :
        // В конструкторе выделяется место для PoolData.
        void* Allocate( size_t );
        // Ну а дальше не важно.
};


Если перегрузить оператор new в базовом Foo к примеру таким образом:
Код

void* Foo::operator new( size_t Size, Pool *pool )
{
    cout << "Foo::operator new(size_t, Pool*) called; pool = " << pool << endl;
    return pool->Allocate(Size);
}

void* Foo::operator new( size_t Size )
{
    cout << "Foo::operator new(size_t) called;" << endl;
    return ::operator new(Size);
}

при выполнении кода, похожего на этот:
new(my_mega_pool) Banana;
вызывается сразу оба оператора. Почему?
Проблему я устранил, убрав перегрузку из моего аналога классу Foo, и инкапсулировав ее в файле Foo.cpp, перегрузив эти операторы у нового класса RealFoo. Тоже абстрактного. В свою очередь, Bar и Banana теперь наследовали у меня RealFoo. Таким образом все заработало как надо. Но почему нельзя было перегрузить эти операторы у Foo?

Автор: Guest 19.5.2005, 07:42
Ну дак как? Никто не поможет?

Автор: chaos 20.5.2005, 06:58
Цитата
Наследование операторов new и delete
Если перегрузить операторы new и delete для некоторого класса, перегруженные версии будут
унаследованы производными классами. Ничто не помешает вам снова перегрузить new и/или delete в
одном из этих производных классов.
Код

class Bar : public Foo {
public:
  virtual ~Bar(); // Foo::~Foo тоже должен быть виртуальным
  void* operator new(size_t bytes);
  void operator delete(void* space, size_t bytes);
};


С виртуальным деструктором все работает. Если деструктор не виртуальный, в следующем фрагменте
будет вызван правильный оператор new и оператор delete базового класса:
Foo* foo = new Bar;
delete foo;
Хотя этот фрагмент работает, подобное переопределение перегруженных операторов обычно считается
дурным тоном. Во всяком случае в кругу знатоков С++ о таких вещах не говорят. Когда производный
класс начинает вмешиваться в управление памятью базового класса, во всей программе начинают
возникать непредвиденные эффекты. Если вам захочется использовать несколько стратегий управления
памятью в одной иерархии классов, лучше сразу включить нужную стратегию в конкретный
производный класс средствами множественного наследования, чем унаследовать ее и потом заявить в
производном классе: «Ха-ха, я пошутил».


Оператор new можно перегрузить так, чтобы помимо размера он вызывался и с другими дополнительными аргументами. Перегрузка лишает стандартной сигнатуры void* operator new(size_t), и, если этого не хочется, ее придется включить в программу вручную.

вот пример:
Код

#include <iostream.h>

#define kPoolSize 4096
struct Pool 
{
    unsigned char* next; // следующий свободный байт
    unsigned char space[kPoolSize];
    Pool() : next(&space[0]) {}
};

class Foo
{
public:
    void* operator new(size_t bytes)
    { 
        return ::operator new(bytes); 
    }

    void* operator new(size_t bytes, Pool* pool)
    {
        void* space = pool->next;
        pool->next += bytes;
        return space;
    }
};

int main()
{
    Pool localPool;
    Foo* foo1 = new Foo; // new по умолчанию
    Foo* foo2 = new(&localPool) Foo; // перегруженный new[s]
    return 0;
}


Здесь клиент, а не класс указывает, где следует разместить объект. Показан лишь фрагмент полной
стратегии. Например, как оператор delete узнает, откуда была взята память — из глобального пула,
используемого оператором new по умолчанию, или настандартного пула, который используется
перегруженным оператором new? Впрочем, основная идея проста: предоставить клиенту класса
некоторую степень контроля над размещением экземпляров в памяти. Это означает, что способ
выделения памяти может выбираться для конкретных объектов и не обязан совпадать для всех
экземпляров класса.
Оператор new можно перегружать с любыми новыми сигнатурами при условии, что все они
различаются, а первым аргументом каждой перегруженной версии является size_t — количество
нужных байт. Перегрузки могут быть как глобальными, так и принадлежать конкретным классам.
Когда компилятор встречает аргументы между new и именем класса, он подставляет размер в начало
списка аргументов и ищет подходящую сигнатуру.


PS и вообще советую почитать книгу
http://anatolix.naumen.ru/Books/CPlusPlus?v=fvi
Jeff Alger (Джефф Элджер)
C++ for Real programmers (C++ Библиотека программиста)
Эта книга заслуживает внимания всех программистов С++!!! smile

Автор: Chaos A.D. 20.5.2005, 19:48
Может я очень непонятливый, но по моему это не совсем то, о чем я спрашивал. У меня вроде итак перегружен оператор new только в одном классе. И его действие распрастраняется на все остальные производные классы. Ну а так как класс Foo у меня абстрактный, для него оператор new никогда не вызывается. Только для RealFoo.

А книгу ту я читал. Тоже присоединяюсь к твоему жирному совету. Книга действительно не плохая.

Автор: chaos 21.5.2005, 08:21
дай тогда кода побольше, посмотреть хоть че там у тебя

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)