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

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

本文关键字:虚拟 接口 C++      更新时间:2023-10-16

我试图在程序中使用一个纯虚拟类作为参数,但是,我遇到了一个编译错误:

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

我想我得到的错误是因为A)不可能实例化抽象类,B)我不能像在C#中使用接口那样使用抽象类

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

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对象,并且该类无法实例化*。这是因为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;
};

*注意,通常可以从派生类型对象构造基类对象。基础对象将由派生对象的基础子对象构造而成(请参见什么是对象切片?)。在您的情况下,这会失败,因为基类型是抽象的

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

std::vector<>存储您添加内容的副本拷贝是一个重要的词。另一个问题是复制构造函数不能是虚拟的(例如,请参阅我们能否在C++中使类复制构造函数成为虚拟的)。

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

请注意,通常情况下,即使Person不是抽象类,您的代码也可能是错误的,因为std::vector<>不会将Employee复制到另一个Employee,它会将Employee复制到切片的Person(这通常称为对象切片)(请记住,复制构造函数不是虚拟的?)。。。

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

与您的问题类似:c++:can vector<基础>包含派生类型的对象?在那里,公认的答案建议使用CCD_ 19。

C#有值类型(structs)和引用类型(classes)。C++只有值类型+该语言设计决策至少有三种不同的解决方案;没有一个是没有问题的。

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

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