覆盖指向抽象类的指针的引用值无效

时间:2014-02-28 23:59:56

标签: c++ pointers abstract-class

我试图用从该类派生的对象覆盖指向抽象类的指针的对象。 但是价值不会改变。 如果我使用指向非抽象类的指针执行相同操作,则一切都按预期工作。

我的猜测是编译器拒绝覆盖内存,因为它不知道在编译时写入的对象的确切大小。

有没有办法让我覆盖对象?

下面是一段代码来展示我的意思。 它使用g ++(Ubuntu / Linaro 4.8.1-10ubuntu9)和clang(3.2-7ubuntu1)进行测试。

#include <stdio.h>

class Abs {
public:
    virtual const char *toString() = 0;
};

class A: public Abs {
public:
    A(const char *s): st(s) {};
    const char *toString() {return st;};
private:
    const char *st;
};

class B: public Abs {
public:
    B(const char *s): st(s) {};
    const char *toString() {return st;};
private:
    const char *st;
};

int main(void) {
    //works:
    A *a = new A("A");
    printf("a: %s\n", a->toString());
    //output: A

    *a = *(new A("B"));
    printf("a: %s\n", a->toString());
    //output: B

    //doesn't work:
    Abs *x;

    x = new A("A");
    printf("x: %s\n", x->toString());
    //output: A

    *x = *(new A("B"));
    printf("x: %s\n", x->toString());
    //output: A

    *x = *(new B("C"));
    printf("x: %s\n", x->toString());
    //output: A

    Abs *y = new A("D");
    *x = *y;
    printf("x: %s\n", x->toString());
    //output: A

    return 0;
}

2 个答案:

答案 0 :(得分:0)

与您的工作代码相当的最近可编译的可能是:

*static_cast<A*>(x) = *new A("A");

请注意,以下是未定义的行为:

*static_cast<B*>(x) = *new B("A");

这是因为派生最多的对象属于A而非B。这是一种更安全的方法:

if (A * p = dynamic_cast<A*>(x)) { *p = *new A("A"); }

答案 1 :(得分:0)

让我们逐步考虑发生的事情。

首先,您定义了指向A的指针,并通过堆中分配的A类对象的地址对其进行初始化

A *a = new A("A");
printf("a: %s\n", a->toString());
//output: A

然后,您为

指向的对象分配了一个新的已分配对象
*a = *(new A("B"));
printf("a: %s\n", a->toString());

在这种情况下,调用复制赋值运算符为类A 隐式定义,并且数据成员st只是从一个对象复制到另一个对象。

因此printf输出字符串文字“B”。

现在你定义了指向Abs

的指针
Abs *x;

并初始化

x = new A("A");
printf("x: %s\n", x->toString());
//output: A

考虑到x的静态类型是Abs

然后在所有这些陈述中

*x = *(new A("B"));
printf("x: %s\n", x->toString());
//output: A

*x = *(new B("C"));
printf("x: %s\n", x->toString());
//output: A

Abs *y = new A("D");
*x = *y;
printf("x: %s\n", x->toString());
//output: A

调用由编译器为类Abs 隐式定义的复制赋值运算符。 Abs类没有数据成员st。所以它没有改变,因为在所有这些情况下都会调用类Abs的复制赋值运算符,因为 x的静态类型是Abs

我想补充一下,即使在这个有效的陈述中

*x = *(new B("C"));

调用类Abs的复制赋值运算符。在这种情况下,在堆中分配的类型B的对象隐式转换为Abs。

类型

如果你想要例如后面的​​陈述

*x = *(new A("B"));

printf输出“B”然后你应该写

*static_cast<A *>( x ) = *(new A("B"));

在这种情况下,将调用A类的复制赋值运算符。

您还可以在抽象类中编写虚拟副本赋值运算符。例如

class Abs {
public:
    virtual const char *toString() = 0;
    virtual Abs & operator =( const Abd & ) { return *this; }
};

并覆盖它,例如在A类

class A: public Abs {
public:
    A(const char *s): st(s) {};
    const char *toString() {return st;};
    A & operator =( const Abs &rhs ) { st = static_cast<const A &>( rhs ).st; return *this; }
private:
    const char *st;
};

在这种情况下,在陈述

之后
*x = *(new A("B"));

下一个语句将输出“B”

printf("x: %s\n", x->toString());
//output: B

注意:我们不考虑您的示例中的内存泄漏。我们正在考虑应用赋值运算符的效果。