比从基类转换为派生类更好的方法

时间:2009-01-05 05:38:35

标签: c++ class-design

我知道像这样的低价无效。我需要一种可行的方法。这是我的问题:我从基类中得到了几个不同的派生类。我的第一个尝试是创建一个基类数组。程序必须选择(或多或少随机)不同的派生类。我曾尝试从基类转换到派生类,将它放在基类的数组中,但显然这不起作用。我真诚地希望采用另一种方法而不是简单地粘贴所有派生类的数组,因为可能会有相当多的派生类。有没有更好的方法来做到这一点,我只是在脑力训练?

如果您需要代码示例或更多信息,请告诉我。这对我来说都是有意义的,但现在已经很晚了,对其他人来说可能没有意义。

非常感谢任何帮助,伙计们。

9 个答案:

答案 0 :(得分:13)

不确定你的意思。听起来像是按值存储对象,而你有一个Base数组。这不起作用,因为只要您分配Derived,该对象就会转换为Base,并且对象的Derived部分会被切掉。但我想你想要一个基数指针数组:

Base * bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i] = new Derived1;
    else if(r == 1)
        bases[i] = new Derived2;
    // ...
}

如果你曾经使用过指针,你就会知道管理它们很麻烦,特别是传递并且不会丢失它们,因为你需要在它们上面调用delete以释放内存并调用析构函数对象。您可以使用shared_ptr,它将为您管理:

shared_ptr<Base> bases[NUM_ITEMS];
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases[i].reset(new Derived1);
    else if(r == 1)
        bases[i].reset(new Derived2);
    // ...
}

现在,您可以将bases[x]传递给另一个shared_ptr,它会注意到您有多个引用 - 如果对象的最后一个引用超出范围,它将自动调用。理想情况下,您还可以用std :: vector:

替换原始数组
std::vector< shared_ptr<Base> > bases;
for(int i=0; i<NUM_ITEMS; i++) {
    int r = get_random_integer();
    if(r == 0)
        bases.push_back(shared_ptr<Base>(new Derived1));
    else if(r == 1)
        bases.push_back(shared_ptr<Base>(new Derived2));
    // ...
}

然后你可以传递向量,并且不会丢失它的大小,你可以根据需要动态添加项目。使用bases.size()获取向量的大小。阅读shared_ptr here

只有在绝对必要时才能从基类转换为派生类。通常,您希望使用一种名为polymorphism的技术,这意味着您在基指针上调用一个函数,但它实际上会调用派生类中定义的函数,具有相同的签名(名称和参数相同)类型)并且据说override它。阅读维基百科上的article。如果你真的需要强制转换,你可以这样做一个原始指针:

Derived1 * d = &dynamic_cast<Derived1&>(*bases[x]);

使用dynamic_cast可确保在转换为错误类型时(即,您所投射的类型不是创建并分配给基本指针的类型),您将获得操作员抛出的异常。对于shared_ptr案例,也有一些方法:

shared_ptr<Derived1> d = dynamic_pointer_cast<Derived1>(bases[x]);
if(d) {
    // conversion successful, it pointed to a derived. d and bases[x] point still 
    // to the same object, thus share it. 
}

答案 1 :(得分:10)

在大多数情况下,从基类转换为派生类是一种糟糕的代码味道。

通过使用虚拟方法,

多态是一种更好的方法。

调用代码应使用基类指针调用方法,允许将其分派到派生类中的相应实现。

我知道这是一个相当通用的答案,但如果没有一些代码片段作为例子,在您的情况下很难推荐更具体的答案。

答案 2 :(得分:1)

使用BaseClass*数组,而不是BaseClass数组。 (或者选择一个智能指针库来为你管理内存。)

答案 3 :(得分:1)

我认为需要更多信息。你为什么要贬低?你对每个派生类都做同样的事情吗?如果是这样,您应该使用接口,或将代码放在基类(或两者)中。

例如,如果您有一组形状(基础),并且想要计算它们的面积,则可以执行以下操作:

    interface IShape
    {
       double GetArea();
    }

    class Square : IShape
    {
       double GetArea()
       {
          return sideLengh*sideLength;
       }
       ...
    }

    class FunnyShape : IShape
    {
       //do funny stuff
       ...
    } 

    ...

void calcTotalArea(List<IShape> shapes)
{
   double total = 0.0;
   foreach (IShape s in shapes)
   {
      total += s.GetArea();
   }
   return total;
}

答案 4 :(得分:1)

我仍然不知道你要做什么。您不能一般地向下转换对象,而是指向对象的指针。通常的方法是使用dynamic_cast(foo),其中foo的类型为bankAccount *。这将为您提供指向MoneyMarket对象或空指针的指针。

然而,对一个对象进行操作的向下转换通常是一个错误;通常通过虚拟继承和多态来做得更好。这是一种将对象分类为不同类型的方法。

如果您想知道兴趣是什么,请将兴趣定义为bankAccount中的虚拟函数,而不支付利息的帐户类型只能返回零。如果您想知道哪些帐户类型是MoneyMarket,那么向下转换可能就是您的选择。

答案 5 :(得分:0)

好的,道歉,我本可以发布一些代码和更多关于我期望的最终结果的信息。

这是我的例子:

class bankAccount
{
   int money;
   bankAccount() {};

   int MoneyInAccount() {return money;};
}

class savingsAccount : public bankAccount
{
   int SavingsInterest();
}

class MoneyMarket : public bankAccount
{
    int CalculateAllPastReturns();
}

然后在我的程序中,我有一个bankAccount数组[40](它不是一个指针)。我希望能够将BankAccount转换为SavingsAccount或MoneyMarket或CheckingAccount等。每个人都有自己独特的类。从那里,该计划可以收集不同的银行账户及其独特信息。

当然,我知道尝试这样做很糟糕,但我不知道该怎么办。

我希望我只是公然错过指针等等。不管怎样,我希望这更具体一点。非常感谢您的帮助!

答案 6 :(得分:0)

如果你有一个bankAccount [40]对象数组,那么你永远不会使用派生类。

转换为派生类非常Java-ish因为你有instanceof运算符而且非常自然。在C ++中,您可以使用reinterpret_cast()来模拟它,但它被认为是糟糕的样式。

你首先要做的是什么?把所有东西都扔进一个容器里?

答案 7 :(得分:0)

这里的问题是您在数组中存储真正的BankAccount对象,而不是SavingsAccount或MoneyMarket对象。你会发现在这些情况下向上投射是行不通的。显然你可以尝试使用reinterpret_cast,但它很可能会破坏。 (想象一下,如果savingsAccount有一个额外的成员变量 - 利率 - 这将使它比基类大几个字节)

你应该做的是存储指向BankAccount对象的指针,这些对象可以是你喜欢的任何派生类 - 你分配一个savingsAccount,并在你的数组中放一个指向它的指针。这很好,编译器很乐意跟踪对象类型,并且已经分配了这些派生类型之一所需的所有内存。

现在,一旦您正确构建了存储或货币市场帐户并存储在某个地方,并在数组中引用它们,您就可以使用dynamic_cast将数组中保存的指针转换为实际类型 - 如果您尝试转换一个这些指针指向错误类型的对象,你会得到一个例外。 (显然,你不想存储savingsAccount对象,然后像访问moneyMarket一样访问它!),如果类型正确,你只能得到一个指向对象的有效指针。您需要打开RTTI才能工作(即编译器将跟踪每个对象的类型)。

除了使用一个好的旧C风格联合之外,实现所需内容的另一种方法是将所有要访问的方法放入基类中,并直接在存储在基类中的基本指针上调用它们。阵列。

输入所有内容之后 - litb说了几乎相同的东西,只比我好得多 - 给他打勾!

答案 8 :(得分:0)

尝试强制转换继承链通常是完全错误的。如果您这样做,那么您很可能没有正确使用继承和多态。如果你发现你需要向上或向下倾斜,那么你有一个有缺陷的课堂设计的可能性很大。

正如已经提到的其他答案,这种方法的方法是在基类中定义所需的所有方法。如果派生类不支持该方法,则将其设置为no-op或使其返回错误。