C++:将抽象类型作为接口返回

C++: Returning abstract type as an interface

我正在尝试通过这段代码实现一些说明,但我无法让它工作。我不能 return 一个 Animal,而且我真的不认为 return 一个 Animal* 是一个好的解决方案,因为我必须 [=13] =] 和 delete 手动。

编辑:我用更具体的代码片段更新了问题,说明了为什么 returning Animal* 不是一个好主意。

正如您从代码片段中看到的,我基本上想使用 Animal 作为 一堆数据的接口,该数据可能是数组的一部分 或者它可能是一个更传统的对象。

编辑 2:工作示例:http://ideone.com/4qp3qT

剩余问题:

#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(CatDog 等)都可以隐藏在后面的东西:指针或引用。如果你事先创建了所有的动物,你可以 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_ptrstd::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_ptrboost::scoped_ptr 或手卷等效项。

您的真正问题是您在任意尝试避免使用可用于该工作的工具(指针、引用或智能指针)。

我猜,这是因为您熟悉某些似乎允许这样做的其他语言。问题是,执行此类操作的语言通过以 C++ 不具备的方式混合对象、引用和指针的概念来实现。

在 C++ 中不可能按值 return 抽象 class。时期。按值返回一个 class 意味着需要对其进行实例化,而抽象 class 是无法实例化的。

如果您的函数 return 是原始引用或指针,则必须在调用者使用它们时对象存在,并且在不再需要时释放(停止存在)对象。这要么意味着您的函数接受管理对象生命周期的责任(例如,对象是数组的一个元素),要么意味着调用者是(例如,函数动态创建对象,调用者在完成后释放它)。

返回智能指针意味着returning 一个管理所包含对象生命周期的对象。该函数创建对象,将其交给智能指针,智能指针被 returned 给调用者。当智能指针不再存在时(例如调用者 returns 超出范围),对象被释放。