1

我知道这样的垂头丧气是行不通的。我需要一种可行的方法。这是我的问题:我有几个不同的派生类都来自一个基类。我的第一次尝试是创建一个基类数组。该程序必须选择(或多或少随机)不同的派生类。我曾尝试从基类转换为派生类,将其放入基类的数组中,但显然这不起作用。我真诚地希望有另一种方法,而不是简单地粘贴所有派生类的数组,因为可能有相当多的派生类。有没有更好的方法来做到这一点,我只是在胡思乱想?

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

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

4

9 回答 9

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,它会注意到您有多个引用 - 如果对对象的最后一个引用超出范围,它将自动调用 delete。理想情况下,您还可以用 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 在这里阅读。

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

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. 
}
于 2009-01-05T05:47:56.090 回答
10

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

多态性,通过使用虚方法,是一种更好的方法。

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

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

于 2009-01-05T05:44:29.200 回答
1

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

于 2009-01-05T05:42:27.380 回答
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;
}
于 2009-01-05T05:50:30.967 回答
1

我仍然不知道你想做什么。一般来说,您不能向下转换对象,而是指向对象的指针。执行此操作的常用方法类似于 dynamic_cast(foo),其中 foo 的类型为 bankAccount *。这将为您提供指向 MoneyMarket 对象的指针或空指针。

但是,向下转换以对对象进行操作通常是错误的。这通常通过虚拟继承和多态性更好地完成。这是一种将对象分类为不同类型的方法。

如果你想知道利息是多少,在bankAccount中将利息定义为一个虚函数,不支付利息的账户类型可以只返回零。如果你想知道哪些账户类型是 MoneyMarket,向下转换可能是要走的路。

于 2009-01-05T15:52:33.450 回答
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 等。每个都有自己独特的类。从那里,该程序可以收集不同的银行账户及其独特信息。

当然,我知道尝试那样投射是不好的,但我不知道该怎么做。

我希望我只是公然遗漏了指针等的东西。无论如何,我希望这更具体一点。非常感谢您的帮助!

于 2009-01-05T15:05:37.383 回答
0

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

转换为派生类是非常 Java 风格的,因为你有 instanceof 运算符,这一切都非常自然。在 C++ 中,您可以使用 reinterpret_cast() 来模拟它,但它被认为是不好的风格。

你一开始想做什么?把所有东西都扔到一个容器里?

于 2009-01-05T15:27:08.027 回答
0

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

您应该做的是存储指向 BankAccount 对象的指针,然后这些指针可以是您喜欢的任何派生类 - 您分配一个 SavingAccount,并将指向它的指针放入您的数组中。这很好用,编译器很高兴地跟踪对象类型,并且您已经分配了这些派生类型之一所需的所有内存。

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

除了使用良好的旧 C 风格联合之外,实现您想要的另一种方法是将您将访问的所有方法放入基类中,并直接在您存储在数组中的基指针上调用它们。

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

于 2009-01-05T15:46:10.233 回答
0

试图向上/向下转换继承链通常是完全错误的。如果您这样做,那么您很可能没有正确使用继承和多态性。如果你发现你需要向上或向下转型,那么很有可能你的职业设计有缺陷。

正如其他答案所提到的,解决这类事情的方法是在基类中定义所有需要的方法。如果派生类不支持该方法,则让它成为空操作或让它返回错误。

于 2009-01-05T16:00:50.313 回答