简体   繁体   中英

C++ : calling an abstract base class constructor / undefined symbol in shared object

I am trying to use abstract classes and I met some problems when defining constructors of derived class. I wrote the following code, based on the answer to this question .

#include <string>
#include <iostream>

class ICommand {
private:
  ICommand();
public:
  const std::string name;
  ICommand(const std::string& name) : name(name) { }
  virtual void callMe(); 
  virtual void callMe2();
};


class MyCommand : public ICommand {
public:
  int x;
  MyCommand(const std::string& name) : ICommand(name) { }
  MyCommand(const std::string& name, int x) : ICommand(name), x(x) { }
  void callMe() { 
    std::cout << name << "\n"; 
  }
  void callMe2() { 
    std::cout << name << x << "\n"; 
  }
};

void f(std::string name) {
  MyCommand A(name);
  A.callMe();
}

This compiles without error. However my aim is to build a .so for a R package). In the R installation process, the .so is build without error, with clang++ -shared , but then there is a verification step which produces

unable to load shared object '/path/object.so':
/path/object.so: undefined symbol: _ZTI8ICommand

I've met this kind of problem before, and there are workarounds — not calling Icommand(name) is fairly simple, but I want to understand what is happening there, and, if possible, how to avoid the workaround.

Thanks in advance for your thoughts.

Answer

For the convenience of future readers: the only necessary change here is to replace the definition of virtual functions in the abstract class by

  virtual void callMe() = 0; 
  virtual void callMe2() = 0;

which makes them pure virtual functions. Why this settles the problem totally beats me.

class ICommand {
private:
  ICommand() = default;
public:
  const std::string name;
  ICommand(const std::string& name) : name(name) { }
  virtual ~ICommand() = default;
  virtual void callMe() = 0; 
  virtual void callMe2() = 0;
};

With the:

class MyClass {
private:
    MyClass();
};

You are deleting a default constructor. If you want to call a default constructor then (declare or) define or don't define one but don't delete it. Your derived class default constructor will call the base class default constructor:

#include <string>
#include <iostream>
#include <memory>

class ICommand {
public:
    std::string name;
    ICommand() : name("The name") { std::cout << "Default base class constructor." << std::endl; }
    virtual void callMe() = 0;
};

class MyCommand : public ICommand {
public:
    MyCommand(){ std::cout << "Default derived class constructor." << std::endl; };
    void callMe() override {
        std::cout << name << std::endl;
    }
};

void f2(const std::string& name) {
    std::shared_ptr<ICommand> p = std::make_shared<MyCommand>();
    p->callMe();
}
int main(){
    f2("asdasd");
}

Part 2:
If you are trying to use the above classes in a polymorphic way then make your ICommand member functions pure virtual:

virtual void callMe() = 0; 
virtual void callMe2() = 0;

Modify the void f function to:

void f(const std::string& name) {
    std::shared_ptr<ICommand> p = std::make_shared<MyCommand>(name);
    p->callMe();
}

Live example on Coliru.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM