【问题标题】:C++ : calling an abstract base class constructor / undefined symbol in shared objectC++:在共享对象中调用抽象基类构造函数/未定义符号
【发布时间】:2017-07-23 11:20:04
【问题描述】:

我正在尝试使用抽象类,但在定义派生类的构造函数时遇到了一些问题。我根据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();
}

这编译没有错误。但是我的目标是为 R 包构建一个 .so )。在 R 安装过程中,.so 构建没有错误,clang++ -shared,但随后有一个验证步骤会产生

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

我以前遇到过这种问题,并且有一些变通方法 - 不调用 Icommand(name) 相当简单,但我想了解那里发生了什么,以及如果可能的话,如何避免变通方法。

提前感谢您的想法。

回答

为了方便以后的读者:这里唯一需要的改动就是把抽象类中虚函数的定义替换为

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

这使它们成为纯虚函数。为什么这完全解决了问题。

【问题讨论】:

  • 您需要为ICommand默认构造函数提供定义。
  • @Ron 我没有代码(这是 R 安装过程的一部分),但事实是共享对象似乎不正确。
  • @Ron 在哪里?抱歉,如果我遗漏了什么,但我只看到默认 ctor 的声明而不是定义。
  • @Elvis 你可以尝试删除ICommand 的默认构造函数的声明吗?它默认被删除,因为你的构造函数删除了它。
  • @Elvis "如果类 X 没有用户声明的构造函数,则没有参数的非显式构造函数被隐式声明为默认值" 来自 [class.ctor]/4 -> 没有隐式默认构造函数的类带任何参数的构造函数

标签: c++ inheritance shared-libraries abstract-class r-package


【解决方案1】:

与:

class MyClass {
private:
    MyClass();
};

您正在删除默认构造函数。如果要调用默认构造函数,则(声明或)定义或不定义一个但不要删除它。你的派生类默认构造函数会调用基类默认构造函数:

#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");
}

第 2 部分:
如果您尝试以多态方式使用上述类,则将您的 ICommand 成员函数设为纯虚拟:

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

修改void f函数为:

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

Live example 在 Coliru 上。

【讨论】:

  • 哇,我被骗了。我必须在代码中添加的只是虚函数的= 0,实际上它与初始化程序无关......(错误消息在这里没有帮助!)。我不明白!但它有效!如果您有时间进一步解释,我会很高兴。
【解决方案2】:
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;
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    • 2015-08-29
    • 2014-08-09
    • 2016-05-05
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    相关资源
    最近更新 更多