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++ 最终更多的是关于正确使用语言,而不是制作能够很好地解决你的问题的软件。
我正在尝试使用纯虚拟 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++ 最终更多的是关于正确使用语言,而不是制作能够很好地解决你的问题的软件。