C++ 与 C# - 使用接口/纯虚拟 类

C++ vs. C# - Using interfaces / pure virtual classes

我正在尝试使用纯虚拟 class 作为程序中的参数,但是,出现编译错误:

Error 1 error C2259: 'Person': cannot instantiate abstract class

我猜我收到的错误是因为 A) 无法实例化抽象 class 和 B) 我不能像使用 C# 中的接口那样使用抽象 class

下面的 C# 程序说明了我试图在 C++ 程序中做什么。 我如何在 C++ 中使用抽象 classes 编写通用代码?如果我被迫使用更专业的 Person 版本,例如员工,代码并不是真正通用的。我必须使用模板吗?

C++ 程序

#include<iostream>
#include<vector>

class Person {
    public:
        virtual std::string getName() = 0;
        virtual void setName(std::string name) = 0;
        virtual std::string toString() = 0;
    private:
        std::string name;
};

class Employee : public Person {
    public:
        std::string getName() {
            return this->name;
        }

        void setName(std::string name) {
            this->name = name;
        }

    std::string toString() {
        return "name:" + this->name;
    }

    private:
        std::string name;
};

class Repository {
    public:
        void add(Person& p) {
            this->repo.push_back(p);
        }
    private:
        std::vector<Person> repo;
};

int main(int argc, char* argv[])
{
    Repository repo;

    Employee emp1;
    emp1.setName("John Doe");

    repo.add(emp1);

    return 0;
}

C# 程序

interface IPerson
{
    string GetName();
    void SetName(string name);
    string ToString();
}

class Employee : IPerson
{
    private string _name;

    public string GetName() {
        return this._name;
    }

    public void SetName(string name) {
        this._name = name;
    }

    public override string ToString() {
        return "name: " + this._name;
    }
}

class Repository
{
    private List<IPerson> _repo;

    public Repository() {
        this._repo = new List<IPerson>();
    }

    public void Add(IPerson p) {
        this._repo.Add(p);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Repository repo = new Repository();

        Employee emp1 = new Employee();
        emp1.SetName("John Doe");

        repo.Add(emp1);
    }
}

问题是Repository正在存储Person对象,而这个class无法实例化*。这是因为 std::vector<Person> 持有 Person .

您可以存储指向 Person 的指针,但您必须确保它们至少与 Repository 实例一样长。例如,

/// class repository does not own Persons it holds
class Repository {
    public:
        void add(Person& p) {
            this->repo.push_back(&p);
        }
    private:
        std::vector<Person*> repo;
};

* 请注意,通常可以从派生类型构造基础 class 对象。基础对象将从派生对象的基础子对象构造(参见 What is object slicing?)。在你的例子中,这失败了,因为基类型是抽象的。

您的问题是 C++ 的内存处理与 C# 的内存处理完全不同。完全完全。

std::vector<> 存储您添加的内容的 副本 份数 是重要的词。另一个问题是复制构造函数不能是虚拟的(参见 Can we make a class copy constructor virtual in C++ 的例子)。

现在...发生的事情是,当您执行 this->repo.push_back(p) 时,std::vector<>Person 中搜索复制构造函数(因为您使用的是 std::vector<Person>)它没有找到它,所以错误。

请注意,一般来说,即使 Person 不是抽象 class,您的代码也可能仍然是错误的,因为 std::vector<> 不会复制 [=17] =] 到另一个 Employee,它会将 Employee 复制到切片 Person(这通常称为 object slicing)(记住复制构造函数不是虚拟的?)...

对于解决方案,请从@juanchopanza 给出的解决方案开始,但请记住这句话 您可以存储指向 Person 的指针,但您必须确保它们至少与 Repository 一样长实例...非常重要!

与您的问题类似:c++: can vector<Base> contain objects of type Derived? 已接受的答案建议使用 std::vector<std::unique_ptr>

C# 有值类型(结构)和引用类型(类)。 C++ 只有值类型 + 至少三种不同的语言设计决策的变通方法; none 其中没有问题。

Boost 在 shared_ptr<T> 方面提供了一个穷人垃圾收集器。使用它,您可以获得多态方面的 C#ish 语义,并且多个名称可以引用同一个实例。这当然也有问题,因为它依赖于引用计数和 RAII,所以它不处理循环依赖;为此,您需要使用 weak_ptr<T> 来打破圆圈。我想这些智能指针中的一些已经进入了最近的 C++ 标准。

你最终会发现,使用 C++ 最终更多的是关于正确使用语言,而不是制作能够很好地解决你的问题的软件。