As long as you compile your program so that shared data structures are binary-equivalent in both the .so lib and your executable, then it should be rather straightforward.
The tutorial linked by Odysseus shows how to do it in Windows.
It seems like you want to initialize an object in your program using the .so lib, with no shared header file.
This is no problem as long as you define the same data structure in the exact same way in your .cpp file. I don't have a Linux machine handy, but I can give you a working example in Windows:
#include <Windows.h>
#include <iostream>
#include <string>
struct ComplexStruct {
std::string m_str;
int m_int;
};
typedef ComplexStruct* (__stdcall* func_complex_ptr)(void);
typedef void(__stdcall* func_complex_ptr2)(ComplexStruct&);
int main() {
HINSTANCE procDll = LoadLibrary(L"C:/temp/test/DllTest.dll");
func_complex_ptr dllComplexFunc = (func_complex_ptr)GetProcAddress(procDll, "getComplex");
ComplexStruct* pComplex = dllComplexFunc();
std::cout << "output1: " << pComplex->m_str << std::endl;
func_complex_ptr2 dllComplexFunc2 = (func_complex_ptr2)GetProcAddress(procDll, "initComplex");
ComplexStruct complexInst;
dllComplexFunc2(complexInst);
std::cout << "output2: " << complexInst.m_str << std::endl;
return 0;
}
(I didn't include the code for the DLL file but I can if you want.)
The only situations that can be problematic are if the object has a complex destructor, depending on what exactly it does, or if the object interacts with hidden state inside of the .so library. You might wish to avoid deleting the object in your program and instead call some form of specialized free function in the .so if it has one. Or if you're dealing with simple objects, then there's no problem.
If you want an example that works on Linux, with .so files, I can craft one as well.