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

时间:2015-02-23 17:17:14

标签: c++ subclassing

我试图通过这段代码实现一些图解,但我无法让它发挥作用。我无法返回Animal,我真的不认为返回Animal*会很好,因为我必须newdelete手动。

编辑:我使用更具体的代码段更新了问题,演示了为什么返回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());
}

3 个答案:

答案 0 :(得分:1)

指针不必指向动态对象,只有它们才需要delete(在这种情况下考虑智能指针)。

在动物的情况下,你可以返回一个参考或指针到笼子里的一只动物。

Animal & GetRandomAnimal() {
    return cats[GetRandomIndex()];
}

对于笼子本身,动态分配是使其具有多态性的最简单方法;但是你应该使用一个智能指针来避免在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或手绘等效内容。

答案 1 :(得分:1)

您真正的问题是,您可以随意尝试避免使用可用于该作业的工具(指针,参考或智能指针)。

我猜,这是因为你熟悉其他似乎允许这种语言的语言。事实是,做这些事情的语言是通过以C ++没有的方式混合对象,引用和指针的概念来实现的。

在C ++中无法按值返回抽象类。期。按值返回类意味着必须实例化它,而抽象类是无法实例化的类。

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

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

答案 2 :(得分:0)

您无法返回Animal,因为您无法创建Animal(它是抽象类型)。你需要任何动物(CatDog等)可以隐藏的东西:指针或引用。如果您事先创建了所有动物,则可以返回(最好是常量)参考:

Cat cat;
Dog dog;

Animal const& GetRandomAnimal()
{
    if (rand() == 42) {
        return cat;
    }
    else {
       return dog;
    }
}

但是,如果要返回之前不存在的动物对象(实例),则唯一的选择是在堆上创建并返回指针。返回指针有一个问题,即不清楚谁负责删除对象。使用C ++ 11,最好的办法是返回一个智能指针:

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)