A Comprehensive Guide to Creating and Using C++ Dynamic Shared Libraries on Linux

Nov 23, 2025 · Programming · 24 views · 7.8

Keywords: C++ | Linux | Dynamic Shared Libraries | dlopen | Extern C

Abstract: This article provides a detailed guide on creating and using C++ dynamic shared libraries on Linux. It covers the complete process from writing library code with extern "C" functions for symbol resolution to dynamically loading and utilizing classes via dlopen and dlsym. Step-by-step code examples and compilation commands are included, along with explanations of key concepts such as position-independent code and virtual functions for proper linking. The tutorial also explores advanced applications like plugin systems, serving as a comprehensive resource for developers building modular and extensible software.

Introduction

Dynamic shared libraries are a fundamental aspect of modular software development on Linux systems. They enable code reuse across multiple applications, reducing memory footprint and facilitating easy updates. In C++, creating shared libraries that contain classes requires careful handling of symbol visibility and linkage to avoid issues with name mangling. This article builds upon existing tutorials to provide a complete example of dynamically creating and using a C++ class library.

Creating the Shared Library

To create a shared library in C++, start by defining a class in a header file. It is essential to use virtual functions for methods that will be called from outside the library to ensure dynamic linkage. Additionally, provide extern "C" functions to create and destroy objects, as C++ name mangling can interfere with symbol resolution.

Here is an example header file example_class.h:

#ifndef EXAMPLE_CLASS_H
#define EXAMPLE_CLASS_H

class ExampleClass {
public:
    ExampleClass();
    virtual void performAction(); // Virtual for dynamic binding

private:
    int value;
};

#endif // EXAMPLE_CLASS_H

Note that in the code blocks, special characters are escaped to prevent HTML parsing errors, such as using &lt; for < and &gt; for >.

Next, the implementation file example_class.cpp:

#include "example_class.h"
#include <iostream>

using namespace std;

extern "C" ExampleClass* create_example() {
    return new ExampleClass;
}

extern "C" void destroy_example(ExampleClass* obj) {
    delete obj;
}

ExampleClass::ExampleClass() : value(42) {}

void ExampleClass::performAction() {
    cout << "Value is: " << value << endl;
}

In this implementation, extern "C" is used to avoid C++ name mangling, ensuring symbols are visible during dynamic linking. The virtual function performAction allows for proper runtime binding.

Using the Shared Library

To use the shared library, employ the dynamic linking functions provided by the dlfcn.h header. Load the library at runtime, retrieve symbols for object creation and destruction, and then use the objects.

Example user code user_main.cpp:

#include <dlfcn.h>
#include <iostream>
#include "example_class.h"

using namespace std;

int main() {
    void* handle = dlopen("./libexample.so", RTLD_LAZY);
    if (!handle) {
        cerr << "Error: " << dlerror() << endl;
        return 1;
    }

    ExampleClass* (*create)() = (ExampleClass* (*)())dlsym(handle, "create_example");
    void (*destroy)(ExampleClass*) = (void (*)(ExampleClass*))dlsym(handle, "destroy_example");

    ExampleClass* obj = create();
    obj->performAction();
    destroy(obj);

    dlclose(handle);
    return 0;
}

This code demonstrates the use of dlopen, dlsym, and dlclose for proper resource management, ensuring the library is loaded and unloaded correctly.

Compilation and Execution

On Linux, compile the shared library with position-independent code and the shared flag:

g++ -fPIC -shared example_class.cpp -o libexample.so

Compile the user program and link with the dynamic linker library:

g++ user_main.cpp -ldl -o user_main

Before running, ensure the library is in the library path or set LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=.
./user_main

This should output: Value is: 42, indicating the library was successfully loaded and used.

Advanced Topics

For plugin systems, define a base class with virtual functions and implement derived classes in the library. The main application loads the library and uses base class pointers, enabling extensibility without recompilation. From the reference article, dynamic linking reduces executable size and allows modular updates. The -fPIC option generates position-independent code, which is essential for shared libraries to be loaded at any address.

Conclusion

Dynamic shared libraries in C++ on Linux enable flexible and efficient code reuse. By using extern "C" for symbol resolution and virtual functions for method calls, developers can create robust libraries. This guide provides a foundation for building and using such libraries in real-world applications, enhancing software maintainability and extensibility.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.