LINUX.ORG.RU

[C++] Перегрузка new и delete в производном классе

 


0

2

Всем привет. Возник один, возможно глупый, вопрос: можно ли перегрузить операторы new и delete именно в производном классе? Если да, то как это работает?

Приведенный ниже код корректно выполняется (gcc)

#include <iostream>
#include <cstdlib>

class Base
{
public:
    Base ()
    {
        std::cout << "base ctor" << std::endl;
    }

    virtual ~Base ()
    {
        std::cout << "base dtor" << std::endl;
    }
};

class Derived: public Base
{
public:
    Derived ()
    {
        std::cout << "derived ctor" << std::endl;
    }

    ~Derived ()
    {
        std::cout << "derived dtor" << std::endl;
    }

    void* operator new (size_t size)
    {
        std::cout << "new" << std::endl;

        return std::malloc(size);
    }

    void operator delete (void *p)
    {
        std::cout << "delete" << std::endl;

        std::free(p);
    }
};

int main (int argc, char* argv[])
{
    Base *b = new Derived;
    delete b;
}


никогда не перегружай new и delete, если нужна кастомная стратегия выделения/освобождения памяти - используй аллокаторы/деаллокаторы

shty ★★★★★
()
Ответ на: комментарий от shty

> никогда не перегружай new и delete

А подробнее/со ссылками можно пояснить, чем плохо перегружать new и delete?

anonymous
()
Ответ на: комментарий от anonymous

> А подробнее/со ссылками можно пояснить, чем плохо перегружать new и delete?

возможны проблемы с библиотеками и конфликтом разных new/delete в рамках одного приложения

aho
()
Ответ на: комментарий от anonymous

А подробнее/со ссылками можно пояснить, чем плохо перегружать new и delete?

1. см. сюда
2. плохо, как минимум то, что не очевидна семантика использования, человек использует стандартный синтаксис и надеется на стандартное поведение на текущей платформе, а в результате получает непонятное поведение, именно поэтому, к примеру, не рекомендуется лишний раз перегружать operator+ и т.д.

shty ★★★★★
()
Ответ на: комментарий от shty

> никогда не перегружай new и delete, если нужна кастомная стратегия выделения/освобождения памяти - используй аллокаторы/деаллокаторы

Спасибо, но вопрос не совсем о том, как правильно писать программы.

mannaz
() автор топика
Ответ на: комментарий от mannaz

Они и перегружаются для конкретного типа. Деструктор виртуален, значит всё нормально, вызовется нужный delete. В чём собственно вопрос?

Booster ★★
()
Ответ на: комментарий от Booster

> Деструктор виртуален, значит всё нормально, вызовется нужный delete

ммм... и давно вызов деструктора в C++ начал освобождать память?

mannaz
() автор топика
Ответ на: комментарий от Booster

> В чём собственно вопрос?

Вопрос все тот же: откуда известно, что при удалении указателя на базовый класс должен вызваться перегруженный в дочернем классе delete (статический метод). Я вполне допускаю, что не догоняю какой-то простой вещи, но пока не совсем понятно.

mannaz
() автор топика
Ответ на: комментарий от mannaz

>ммм... и давно вызов деструктора в C++ начал освобождать память?

При вызове оператора delete, деструктор определяет какому классу принадлежит это оператор delete, соответственно если не определить деструктор виртуальным, то он может промахнуться. То что деструктор освобождает память, я нигде не писал.

Booster ★★
()
Ответ на: комментарий от Booster

> При вызове оператора delete, деструктор определяет какому классу принадлежит это оператор delete, соответственно если не определить деструктор виртуальным, то он может промахнуться.

Судя по всему, так и происходит. Я, честно говоря, сначала подумал, что это какие-то сайд-эффекты оптимизации компилятора, т.к. приведенный пример слишком простой. Гугл не помог, читать стандарт было лень, поэтому эмпирически проверил кейс в более реалистичной ситуации с динамическими библиотеками и без инлайнов на разных компиляторах :) Так же, как ты и сказал, при не виртуальном деструкторе корректного вызова delete не происходит.

Не знаю, возможно, я заработался и это давно известно всем, но из ситуации есть одно важное следствие, а именно возможность реализовать пулирование для объектов, логика владения которыми лежит вне области кода, расширяющего предоставляемые извне классы (фреймворк какой-нибудь)

mannaz
() автор топика
Ответ на: комментарий от mannaz

возможность реализовать пулирование для объектов, логика владения которыми лежит вне области кода, расширяющего предоставляемые извне классы (фреймворк какой-нибудь)

сначала написал другой комментарий, потом задумался... чувак ты предлагаешь какую-то ересь как мне кажется, ну или чуднО формулируешь свои мысли

shty ★★★★★
()
Ответ на: комментарий от shty

чувак ты предлагаешь какую-то ересь как мне кажется, ну или чуднО формулируешь свои мысли

Чувак, надеюсь, так будет понятнее (сорри, компиляцию не проверял):

#include <QCoreApplication>
#include <QEvent>
#include <QtDebug>

// Custom event class
class Event: public QEvent
{
public:
    Event ();
    ~Event ();
    
    void* operator new (size_t size);
    void operator delete (void *p);
};

Event::Event () :
    QEvent(QEvent::User)
{
    qDebug() << "ctor";
}

Event::~Event ()
{
    qDebug() << "dtor";
}

void* Event::operator new (size_t size)
{
    qDebug() << "new";
    
    // TODO: Use events pool here
    return malloc(size);
}

void Event::operator delete (void *p)
{
    qDebug() << "free";

    // TODO: Use events pool here    
    free(p);
}

// Object receiving custom events
class Receiver: public QObject
{
public:
    explicit Receiver (QObject *parent = 0);

    // QObject
    virtual bool event (QEvent *event);
};

Receiver::Receiver (QObject *parent) :
    QObject(parent)
{
}
        
bool Receiver::event (QEvent *event)
{
    if (event->type() == QEvent::User)
    {
        qDebug() << "event";

        QCoreApplication::instance()->quit();
        
        return true;
    }
    else
        return QObject::event(event);
};

int main (int argc, char* argv[])
{
    QCoreApplication app(argc, argv);

    // Allocating and posting event to receiver (event object will be destroyed by event loop once delivered and processed)
    Receiver recv;
    app.postEvent(&recv, new Event);

    // Entering event loop
    return app.exec();
}
mannaz
() автор топика
Ответ на: комментарий от shty

> плохо, как минимум то, что не очевидна семантика использования, человек использует стандартный синтаксис и надеется на стандартное поведение

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

const86 ★★★★★
()
Ответ на: комментарий от const86

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

тогда вообще зачем его писать, такой new уже есть

shty ★★★★★
()
Ответ на: комментарий от shty

> тогда вообще зачем его писать, такой new уже есть

В большинстве случаев так и есть :) Тот же пул объектов вполне можно реализовать через перегрузку new/delete.

const86 ★★★★★
()
Ответ на: комментарий от const86

> Тот же пул объектов вполне можно реализовать через перегрузку new/delete.

Ну да, товарищ выше уже написал эту идею.

const86 ★★★★★
()
Ответ на: комментарий от const86

Тот же пул объектов вполне можно реализовать через перегрузку new/delete.

ну вот так через аксессор для клизьмы делают, а потом говорят, что С++ кривой

shty ★★★★★
()
Ответ на: комментарий от shty

> некогда вдумчиво парсить, но имхо - трава

согласен, сп**дануть «юзай аллокаторы», «ты желаешь странного» и «ты еще Qt не видел» гораздо проще

mannaz
() автор топика
Ответ на: комментарий от shty

> ну вот так через аксессор для клизьмы делают, а потом говорят, что С++ кривой

Никто не говорит, что перегруз new/delete для пула объектов - лучший путь. Но в некоторых ситуациях по-другому не сделать (см. пример выше).

mannaz
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.