如何处理在接受基类数组的函数中将派生类数组作为参数传递?

时间:2016-09-20 10:12:51

标签: c++ arrays polymorphism pass-by-reference slice

我在下面有两个类的示例代码:

class B {
public:
    int b;
};

class D : public B {
public:
    int d;
};

我有一个函数接受B作为数组并总结b成员:

int sum(B a[], int count){

    int sum = 0;

    for(int i = 0; i < count; i++) {
        sum += a[i].b;
    }

    return sum;
}

这是我的主要内容:

int main() {

    D* a = new D[3];

    for(int i = 0; i < 3; i++)
        a[i].b = i+1;

    cout << "Sum is " << sum(a, 3) << endl;
    return 0;
}

我所知道的是这段代码由于切片而无效。我没有得到正确的输出,应该是6.我试图将sum函数中的参数作为:

int sum(B& a[], int count)

传递一个引用,但这会产生编译错误,因为你不能使用引用数组。

我的问题是,最好的方法是什么?

目前我的解决方案是将我的数组声明为:

B* a = new D[3];

但它并不完全清楚为什么它不适合以前的实施。

2 个答案:

答案 0 :(得分:0)

D数组永远不会像B数组,因为这些类型的大小不同。 D的大小大于B,因此D数组中的连续元素将比{B中的连续元素间隔更远(因为每个元素占用更多空间) 1}}数组。

如果要使用子类型多态,则需要使用指针(或引用),而不是对象类型本身。你不能有一个引用数组,所以你必须使用一个指针数组。

这样的事情会起作用:

int sum(B *a[], int count) {

    int sum = 0;

    for(int i = 0; i < count; i++) {
        sum += a[i]->b;
    }

    return sum;
}

int main() {
    D *a[3];

    for (int i = 0; i < 3; i++) {
        a[i] = new D;
        a[i]->b = i+1;
    }

    cout << "Sum is " << sum((B **)a, 3) << endl;

    for (int i = 0; i < 3; i++) {
      delete a[i];
    }
    return 0;
}

请注意,我仍然需要从D **投射到B **,因为转换仍然可能导致潜在的不安全因素。如果您已将数组声明为B *[3],则不需要强制转换:

int sum(B *a[], int count) {

    int sum = 0;

    for(int i = 0; i < count; i++) {
        sum += a[i]->b;
    }

    return sum;
}

int main() {
    B *a[3];

    for (int i = 0; i < 3; i++) {
        a[i] = new D;
        a[i]->b = i+1;
    }

    cout << "Sum is " << sum(a, 3) << endl;

    for (int i = 0; i < 3; i++) {
      delete a[i];
    }
    return 0;
}

或者您可以使用模板:

template <class T>
int sum(T *a[], int count) {

    int sum = 0;

    for(int i = 0; i < count; i++) {
        sum += a[i]->b;
    }

    return sum;
}

int main() {
    D *a[3];

    for (int i = 0; i < 3; i++) {
        a[i] = new D;
        a[i]->b = i+1;
    }

    cout << "Sum is " << sum(a, 3) << endl;

    for (int i = 0; i < 3; i++) {
      delete a[i];
    }
    return 0;
}

模板也适用于您原来的非指针式方法:

template <class T>
int sum(T a[], int count) {

    int sum = 0;

    for(int i = 0; i < count; i++) {
        sum += a[i].b;
    }

    return sum;
}

int main() {
    D a[3];

    for (int i = 0; i < 3; i++) {
        a[i].b = i+1;
    }

    cout << "Sum is " << sum(a, 3) << endl;
    return 0;
}

答案 1 :(得分:-1)

您提出的解决方案实际上是正确的,但您可能希望使用virtual关键字进行B抽象。 另一种方法是重新定义sum函数并使其接受D对象。

解释原始实现无法解决的原因的直观方法如下:

在总和中我们有这行代码:

sum += a[i].b;其中a是指向B类对象的指针。

这意味着为了获得索引i的元素,我们需要添加 i * sizeof(B)到基地址a。但是这个地址现在错了,而我们需要地址i * sizeof(D)

通过将指针类型声明为B,此问题已修复。

顺便说一下,你可能也想让你的b变量受到保护而d变为私有,然后提供getter。