наследование — C ++: вызов абстрактного конструктора базового класса / неопределенного символа в общем объекте

Я пытаюсь использовать абстрактные классы, и я столкнулся с некоторыми проблемами при определении конструкторов производного класса. Я написал следующий код, основанный на ответе на этот вопрос.

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

Это компилируется без ошибок. Однако моя цель — создать .so для пакета R). В процессе установки 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;

что делает их чисто виртуальными функциями. Почему это решает проблему полностью меня бьет.

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();
}

Живой пример на Колиру.

1

Другие решения

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;
};
1

По вопросам рекламы [email protected]