为什么不叫我的新操作员

phl*_*psy 8 c++ overriding allocation g++

我想看到一个动态加载的库(加载了dlopen等)确实使用了自己的新的删除操作符而不是调用程序中定义的那些操作符.所以我写了下面的library.cpp

#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;
    }
}
Run Code Online (Sandbox Code Playgroud)

用它编译

g++ -g -Wall -fPIC -shared library.cpp -o library.so
Run Code Online (Sandbox Code Playgroud)

或者像俄罗斯雇员建议尝试的那样(但最终没有改变)

g++ -g -Wall -fPIC -shared -Wl,-Bsymbolic library.cpp -o library.so
Run Code Online (Sandbox Code Playgroud)

类Base只保存一个int值和一个函数get_value()来获取该值.之后我写了这样的client.cpp

#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;
}
Run Code Online (Sandbox Code Playgroud)

用它编译

g++ -Wall -g -o client -ldl client.cpp
Run Code Online (Sandbox Code Playgroud)

执行客户端我只获得"新客户端调用"和"删除客户端调用".即使我使用编译器开关-Bsymbolic为库,如Employed Russian建议.

现在:出了什么问题?我以为共享库正在使用自己的new/delete,因此你必须在工厂旁边提供在库代码中创建析构函数destroy.

补充问题:为什么我需要destroy(Base*p)功能?如果这个函数只调用客户端的delete-operator,我也可以自己做,即在最后一行的"删除a"而不是destroy_module(a).

我发现答案:库也可以提供新的/删除操作符对.因此,如果我首先使用库的新版本以及稍后客户端的删除,我可能会陷入陷阱.可悲的是,直到现在我从未看到我的图书馆使用它自己的新的或删除...所以原来的问题仍然没有得到解答.

补充:我只是指Linux平台.

编辑:重要的部分是在雇用俄罗斯答案的评论中.所以我简单地给出了主要线索:如果用这种方式调用gcc

g++ -Wall -g -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
Run Code Online (Sandbox Code Playgroud)

该库将使用它自己的新/删除操作符.否则结果

g++ -Wall -g -fPIC -shared library.cpp -o library.so
Run Code Online (Sandbox Code Playgroud)

在使用调用程序的新/删除运算符的库中.感谢就业俄语!

Emp*_*ian 11

问题是,在大多数UNIX平台(不像Win32AIX)默认绑定的所有符号引用第一个符号到运行时加载可见的定义.

如果你'operator new'在main中定义a.out,一切都将绑定到该定义(如Neil Butterworth的示例所示),因为它a.out是第一个图像运行时加载器搜索.

如果您在一个库中定义它libC.so(或者libstdc++.so在您使用的情况下GCC),那么您的定义将永远不会被使用.由于您dlopen()在程序启动后进入库,libC因此已经加载了该库,并且您的库是运行时加载程序将搜索的最后一个库; 所以你输了.

ELF平台上,您可以使用更改默认行为-Bsymbolic.来自man ldLinux:

 -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.
Run Code Online (Sandbox Code Playgroud)

注意,这-Bsymbolic是一个链接器标志,而不是编译器标志.如果使用g++,您必须将标志传递给链接器,如下所示:

  g++ -fPIC -shared library.cpp -o library.so -Wl,-Bsymbolic
Run Code Online (Sandbox Code Playgroud)