C++:将抽象类型作为接口返回
C++: Returning abstract type as an interface
我正在尝试通过这段代码实现一些说明,但我无法让它工作。我不能 return 一个 Animal
,而且我真的不认为 return 一个 Animal*
是一个好的解决方案,因为我必须 [=13] =] 和 delete
手动。
编辑:我用更具体的代码片段更新了问题,说明了为什么 returning Animal*
不是一个好主意。
正如您从代码片段中看到的,我基本上想使用 Animal
作为
一堆数据的接口,该数据可能是数组的一部分
或者它可能是一个更传统的对象。
编辑 2:工作示例:http://ideone.com/4qp3qT
剩余问题:
- 重复代码 (
FeedAnimals()
)
- 性能?
- 必须
delete
手动(对于猫)
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <stdlib.h>
using namespace std;
class Animal {
virtual void eat() = 0;
};
// Dog is more high-level
class Dog: public Animal {
char attr1;
char attr2;
Dog(_attr1, _attr2):
attr1(_attr1),
attr2(_attr2)
{}
Dog():
attr1('d'),
attr2('g')
{}
void eat() {
cout << "Dog ate food (" << attr1 << attr2 << ").\n";
}
void barf() {
cout << "Whaf";
}
};
// Cat is more low-level
class Cat: public Animal {
// A cat can basically only exist in a cage
// and it's attributes are defined by it's location
// in the Cage's underlying array. A Cat should also
// have to 2 attributes (both char), so this would
// be kind of true:
// sizeof(Cat) == 2
char* ptr;
// I will only use a Cat in a context of arrays, so
// no need to care about memory management here.
Cat(char* _ptr):
ptr(_ptr)
{}
void eat() {
cout << "Cat ate food (" << *ptr << *(ptr+1) << ").\n";
}
void meow() {
cout << "Meow.";
}
};
class Cage {
virtual Animal GetRandomAnimal() = 0;
};
// DogCage uses a nice (more high level) vector
class DogCage {
vector<Dog> dogs;
DogCage():
dogs(5)
{}
Animal GetRandomAnimal() {
// ?
}
}
// CatCage uses a more low level pointer together with
// malloc etc.
class CatCage {
char* cats;
CatCage():
cats((char*) malloc(4*2)) // room for 4 cats
{}
~CatCage() {
free(cats);
}
Animal GetRandomAnimal() {
// ...
// This is why it's difficult to return a pointer
// to an Animal. I basically want to use a Cat as
// a simple interface to a part of an array.
// If I use pointers etc, that seems like it would
// hit performance.
return Cat(cats+(random*2))
}
}
void FeedAnimals(Animal& a) {
a.eat();
}
int main() {
Cage cage; // ?
string s;
cout << "Cats or dogs?";
cin >> s;
if (s=="Cats") {
cage = CatCage(); // ?
} else {
cage = DogCage(); // ?
}
// fill cage with animals (either cats or dogs)
FeedAnimals(cage.GetRandomAnimal());
}
你不能 return 一个 Animal
因为你不能创建一个 Animal
(它是一个抽象类型)。您需要任何 Animal(Cat
、Dog
等)都可以隐藏在后面的东西:指针或引用。如果你事先创建了所有的动物,你可以 return 一个(最好是 const)参考:
Cat cat;
Dog dog;
Animal const& GetRandomAnimal()
{
if (rand() == 42) {
return cat;
}
else {
return dog;
}
}
但是,如果您想 return 一个以前不存在的动物对象(实例),您唯一的选择是在堆上创建并 return 一个指针。返回一个指针有一个问题就是不清楚谁负责删除对象。对于 C++11,最好的选择是 return 智能指针:
std::unique_ptr<Animal> GetRandomAnimal()
{
if (rand() == 42) {
return std::make_unique<Cat>(/*args to the cat constructor*/);
}
else {
return std::make_unique<Dog>(/*args to the dog constructor*/);
}
}
或使用 std::shared_ptr
和 std::make_shared
。
(唉,std::make_unique 是 C++14)
指针不必指向动态对象,如果指向动态对象则只需要 delete
(在这种情况下考虑使用智能指针)。
对于动物,您可以 return 引用或指向笼子中的一只动物。
Animal & GetRandomAnimal() {
return cats[GetRandomIndex()];
}
对于cage本身来说,动态分配是使其多态化最简单的方式;但是你应该使用一个智能指针来避免使用 delete
和调试内存泄漏。
std::unique_ptr<Cage> cage;
if (s=="Cats") {
cage.reset(new CatCage); // C++14: cage = std::make_unique<CatCage>();
} else {
cage.reset(new DogCage); // C++14: cage = std::make_unique<DogCage>();
}
如果您还停留在过去,请将 unique_ptr
替换为 auto_ptr
、boost::scoped_ptr
或手卷等效项。
您的真正问题是您在任意尝试避免使用可用于该工作的工具(指针、引用或智能指针)。
我猜,这是因为您熟悉某些似乎允许这样做的其他语言。问题是,执行此类操作的语言通过以 C++ 不具备的方式混合对象、引用和指针的概念来实现。
在 C++ 中不可能按值 return 抽象 class。时期。按值返回一个 class 意味着需要对其进行实例化,而抽象 class 是无法实例化的。
如果您的函数 return 是原始引用或指针,则必须在调用者使用它们时对象存在,并且在不再需要时释放(停止存在)对象。这要么意味着您的函数接受管理对象生命周期的责任(例如,对象是数组的一个元素),要么意味着调用者是(例如,函数动态创建对象,调用者在完成后释放它)。
返回智能指针意味着returning 一个管理所包含对象生命周期的对象。该函数创建对象,将其交给智能指针,智能指针被 returned 给调用者。当智能指针不再存在时(例如调用者 returns 超出范围),对象被释放。
我正在尝试通过这段代码实现一些说明,但我无法让它工作。我不能 return 一个 Animal
,而且我真的不认为 return 一个 Animal*
是一个好的解决方案,因为我必须 [=13] =] 和 delete
手动。
编辑:我用更具体的代码片段更新了问题,说明了为什么 returning Animal*
不是一个好主意。
正如您从代码片段中看到的,我基本上想使用 Animal
作为
一堆数据的接口,该数据可能是数组的一部分
或者它可能是一个更传统的对象。
编辑 2:工作示例:http://ideone.com/4qp3qT
剩余问题:
- 重复代码 (
FeedAnimals()
) - 性能?
- 必须
delete
手动(对于猫)
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <stdlib.h>
using namespace std;
class Animal {
virtual void eat() = 0;
};
// Dog is more high-level
class Dog: public Animal {
char attr1;
char attr2;
Dog(_attr1, _attr2):
attr1(_attr1),
attr2(_attr2)
{}
Dog():
attr1('d'),
attr2('g')
{}
void eat() {
cout << "Dog ate food (" << attr1 << attr2 << ").\n";
}
void barf() {
cout << "Whaf";
}
};
// Cat is more low-level
class Cat: public Animal {
// A cat can basically only exist in a cage
// and it's attributes are defined by it's location
// in the Cage's underlying array. A Cat should also
// have to 2 attributes (both char), so this would
// be kind of true:
// sizeof(Cat) == 2
char* ptr;
// I will only use a Cat in a context of arrays, so
// no need to care about memory management here.
Cat(char* _ptr):
ptr(_ptr)
{}
void eat() {
cout << "Cat ate food (" << *ptr << *(ptr+1) << ").\n";
}
void meow() {
cout << "Meow.";
}
};
class Cage {
virtual Animal GetRandomAnimal() = 0;
};
// DogCage uses a nice (more high level) vector
class DogCage {
vector<Dog> dogs;
DogCage():
dogs(5)
{}
Animal GetRandomAnimal() {
// ?
}
}
// CatCage uses a more low level pointer together with
// malloc etc.
class CatCage {
char* cats;
CatCage():
cats((char*) malloc(4*2)) // room for 4 cats
{}
~CatCage() {
free(cats);
}
Animal GetRandomAnimal() {
// ...
// This is why it's difficult to return a pointer
// to an Animal. I basically want to use a Cat as
// a simple interface to a part of an array.
// If I use pointers etc, that seems like it would
// hit performance.
return Cat(cats+(random*2))
}
}
void FeedAnimals(Animal& a) {
a.eat();
}
int main() {
Cage cage; // ?
string s;
cout << "Cats or dogs?";
cin >> s;
if (s=="Cats") {
cage = CatCage(); // ?
} else {
cage = DogCage(); // ?
}
// fill cage with animals (either cats or dogs)
FeedAnimals(cage.GetRandomAnimal());
}
你不能 return 一个 Animal
因为你不能创建一个 Animal
(它是一个抽象类型)。您需要任何 Animal(Cat
、Dog
等)都可以隐藏在后面的东西:指针或引用。如果你事先创建了所有的动物,你可以 return 一个(最好是 const)参考:
Cat cat;
Dog dog;
Animal const& GetRandomAnimal()
{
if (rand() == 42) {
return cat;
}
else {
return dog;
}
}
但是,如果您想 return 一个以前不存在的动物对象(实例),您唯一的选择是在堆上创建并 return 一个指针。返回一个指针有一个问题就是不清楚谁负责删除对象。对于 C++11,最好的选择是 return 智能指针:
std::unique_ptr<Animal> GetRandomAnimal()
{
if (rand() == 42) {
return std::make_unique<Cat>(/*args to the cat constructor*/);
}
else {
return std::make_unique<Dog>(/*args to the dog constructor*/);
}
}
或使用 std::shared_ptr
和 std::make_shared
。
(唉,std::make_unique 是 C++14)
指针不必指向动态对象,如果指向动态对象则只需要 delete
(在这种情况下考虑使用智能指针)。
对于动物,您可以 return 引用或指向笼子中的一只动物。
Animal & GetRandomAnimal() {
return cats[GetRandomIndex()];
}
对于cage本身来说,动态分配是使其多态化最简单的方式;但是你应该使用一个智能指针来避免使用 delete
和调试内存泄漏。
std::unique_ptr<Cage> cage;
if (s=="Cats") {
cage.reset(new CatCage); // C++14: cage = std::make_unique<CatCage>();
} else {
cage.reset(new DogCage); // C++14: cage = std::make_unique<DogCage>();
}
如果您还停留在过去,请将 unique_ptr
替换为 auto_ptr
、boost::scoped_ptr
或手卷等效项。
您的真正问题是您在任意尝试避免使用可用于该工作的工具(指针、引用或智能指针)。
我猜,这是因为您熟悉某些似乎允许这样做的其他语言。问题是,执行此类操作的语言通过以 C++ 不具备的方式混合对象、引用和指针的概念来实现。
在 C++ 中不可能按值 return 抽象 class。时期。按值返回一个 class 意味着需要对其进行实例化,而抽象 class 是无法实例化的。
如果您的函数 return 是原始引用或指针,则必须在调用者使用它们时对象存在,并且在不再需要时释放(停止存在)对象。这要么意味着您的函数接受管理对象生命周期的责任(例如,对象是数组的一个元素),要么意味着调用者是(例如,函数动态创建对象,调用者在完成后释放它)。
返回智能指针意味着returning 一个管理所包含对象生命周期的对象。该函数创建对象,将其交给智能指针,智能指针被 returned 给调用者。当智能指针不再存在时(例如调用者 returns 超出范围),对象被释放。