Вопрос по c++ – Почему мой новый оператор не называется
Я хотел видеть, что динамически загружаемая библиотека (загруженная с помощью dlopen и т. Д.) Действительно использует свои новые операторы удаления, а не те, которые определены в вызывающей программе. Поэтому я написал следующую библиотеку .cpp
<code>#include <exception> #include <new> #include <cstdlib> #include <cstdio> #include "base.hpp" void* operator new(size_t size) { std::printf("New of library called\n"); void *p=std::malloc(size); if (p == 0) // did malloc succeed? throw std::bad_alloc(); // ANSI/ISO compliant behavior return p; } void operator delete(void* p) { std::printf("Delete of library called\n"); std::free(p); } class Derived : public Base { public: Derived() : Base(10) { } }; extern "C" { Base* create() { return new Derived; } void destroy(Base* p) { delete p; } } </code>
и скомпилировал это с
<code>g++ -g -Wall -fPIC -shared library.cpp -o library.so </code>
или как занятый русский предложил попробовать (но в итоге ничего не изменилось)
<code>g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so </code>
Класс Base содержит только значение типа int и функцию get_value () для получения этого значения. После этого я написал client.cpp вот так
<code>#include <exception> #include <new> #include <iostream> #include <cstdlib> #include <cstdio> #include <dlfcn.h> #include "base.hpp" void* operator new(size_t size) { std::printf("New of client called\n"); void *p=std::malloc(size); if (p == 0) // did malloc succeed? throw std::bad_alloc(); // ANSI/ISO compliant behavior return p; } void operator delete(void* p) { std::printf("Delete of client called\n"); std::free(p); } typedef Base* create_module_t(); typedef void destroy_module_t(Base *); int main() { void* handle = dlopen("./library.so", RTLD_LAZY); if (handle == NULL) { std::cout << dlerror() << std::endl; return 1; } create_module_t* create_module = NULL; void* func = dlsym(handle, "create"); if (func == NULL) { std::cout << dlerror() << std::endl; return 1; } else create_module = (create_module_t *)func; destroy_module_t* destroy_module = NULL; func = dlsym(handle, "destroy"); if (func == NULL) { std::cout << dlerror() << std::endl; return 1; } else destroy_module = (destroy_module_t *)func; Base* a = create_module(); std::cout << "Value: " << a->get_value() << std::endl; destroy_module(a); return 0; } </code>
и скомпилировал это с
<code>g++ -Wall -g -o client -ldl client.cpp </code>
При выполнении клиента я получаю только «Новый клиент под названием» и "Удалить клиента с именем". Даже если я использую переключатель компилятора -Bsymbolic для библиотеки, как предложил Employed Russian.
Теперь: что пошло не так? Я думал, что разделяемые библиотеки используют свои собственные новые / удаления, и поэтому вы должны предоставить рядом с фабрикой создать деструктор уничтожить в коде библиотеки.
Дополнительный вопрос: зачем мне функция destroy (Base * p)? Если бы эта функция вызывала только оператор удаления клиента, я мог бы также сделать это сам, т.е. вместо destroy_module (a) рядом с последней строкой.
Ответ, который я нашел: Библиотека также может предоставить новую пару / оператор удаления. Так что, если я сначала использую новую библиотеку, а затем удаляю клиента, я, вероятно, попаду в ловушку. К сожалению, до сих пор я никогда не видел свою библиотеку, использующую ее новую или удаленную ... Так что на оригинальный вопрос до сих пор нет ответа.
Дополнение: я имею в виду только платформу Linux.
Изменить: Важные части находятся в комментариях к Ответу занятого русского языка. Итак, я даю основную подсказку в двух словах: если так назвать gcc
<code>g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic </code>
библиотека будет использовать собственные операторы new / delete. В противном случае результаты
<code>g++ -Wall -g -fPIC -shared library.cpp -o library.so </code>
в библиотеке, которая использует операторы new / delete вызывающей программы. Спасибо занятым русским!
Проблема в том, что на большинствеUNIX
платформы (в отличие отWin32
а такжеAIX
) все ссылки на символы по умолчанию связаны сfirst определение символа, видимого для загрузчика во время выполнения.
Если вы определите'operator new'
в основномa.out
все будет привязано к этому определению (как показывает пример Нейла Баттерворта), потому чтоa.out
это самый первый поиск загрузчика во время выполнения изображения.
Если вы определите его в библиотеке, которая загружается послеlibC.so
(или жеlibstdc++.so
если вы используетеGCC
), тогда ваше определение никогда не будет использовано. Так как выdlopen()
после запуска программы,libC
к этому моменту уже загружен, и ваша библиотека является самой последней, которую будет искать загрузчик времени выполнения; так что ты проиграл.
НаELF
платформы, вы можете изменить поведение по умолчанию, используя-Bsymbolic
, Отman ld
в Linux:
-Bsymbolic
When creating a shared library, bind references to global symbols
to the definition within the shared library, if any. Normally, it
is possible for a program linked against a shared library to override
the definition within the shared library. This option is only meaningful
on ELF platforms which support shared libraries.
Обратите внимание, что-Bsymbolic
это флаг компоновщика, а не флаг компилятора. При использованииg++
Вы должны передать флаг компоновщику следующим образом:
g++ -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
Следующий код работает как положено. Ожидаете ли вы, что код вашей динамической библиотеки будет использовать предоставленную вами новую / удаленную версию? Я думаю, вы будете разочарованы.
#include <memory>
#include <cstdio>
#include <cstdlib>
using namespace std;;
void* operator new(size_t size) {
std::printf("New...\n");
void *p=std::malloc(size);
if (p == 0) // did malloc succeed?
throw std::bad_alloc(); // ANSI/ISO compliant behavior
return p;
}
void operator delete(void* p) {
std::printf("Delete...\n");
std::free(p);
}
int main() {
int * p = new int(42);
delete p;
}
Я думаю, проблема в том, что перегрузка операторов в C ++ - это функция времени компиляции, а не ссылки. DLL была скомпилирована без знания перегруженной функции new (), поэтому она не будет работать правильно.
Другая возможность состоит в том, что на вашей платформе он будет работать с использованием ссылок (как в вашем примере), но DLL не разрешает символы из вашего исполняемого файла.