我知道像这样的低价无效。我需要一种可行的方法。这是我的问题:我从基类中得到了几个不同的派生类。我的第一个尝试是创建一个基类数组。程序必须选择(或多或少随机)不同的派生类。我曾尝试从基类转换到派生类,将它放在基类的数组中,但显然这不起作用。我真诚地希望采用另一种方法而不是简单地粘贴所有派生类的数组,因为可能会有相当多的派生类。有没有更好的方法来做到这一点,我只是在脑力训练?
如果您需要代码示例或更多信息,请告诉我。这对我来说都是有意义的,但现在已经很晚了,对其他人来说可能没有意义。
非常感谢任何帮助,伙计们。
答案 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或使其返回错误。