类型转换的C ++赋值

时间:2011-12-20 01:49:21

标签: c++ casting

我偶然发现了类似的东西,然后尝试了一些事情并注意到以下内容似乎在G ++中是合法的:

struct A {
    int val_;
    A() { }
    A(int val) : val_(val) { }
    const A& operator=(int val) { val_ = val; return *this; }
    int get() { return val_; }
};

struct B : public A {
    A getA() { return (((A)*this) = 20); } // legal?
};

int main() {
    A a = 10;
    B b;
    A c = b.getA();
}

所以B::getB返回一个A类型,然后将20赋值给自己(通过重载的A::operator=)。

经过几次测试后,似乎它返回了正确的值(c.get会返回20,这可能是人们所期望的。

所以我想知道,这是未定义的行为吗?如果是这样的话,究竟是什么原因呢?如果没有,这样的代码有什么好处?

2 个答案:

答案 0 :(得分:3)

经过仔细检查,在@Kerrek SB和@Aaron McDaid的帮助下,以下内容:

return (((A)*this) = 20);

...就像:

的简写(但模糊)语法
A a(*this); 
return a.operator=(20);

......甚至更好:

return A(*this) = 20;

...因此被定义为行为。

答案 1 :(得分:1)

这里有许多完全不同的事情。代码有效,但您在问题中做出了错误的假设。你说

" B :: getA返回[...],之后将值20分配给本身"

(我的重点)这不正确。 getA 修改对象。要验证这一点,您只需将const放在方法签名中即可。然后我会完全解释。

A getA() const {
    cout << this << " in getA() now" << endl;
    return (((A)*this) = 20);
}

那么这里发生了什么?查看我的sample code(我已将我的成绩单复制到此答案的末尾):

A a = 10;

这声明了一个带有构造函数的A.挺直的。下一行:

B b; b.val_ = 15;

B没有任何构造函数,所以我必须直接写入其val_成员(继承自A)。

在我们考虑下一行A c = b.getA();之前,我们必须非常仔细地考虑更简单的表达式:

b.getA();

这不会修改b,虽然它可能看起来像它。

最后,我的示例代码打印出b.val_,你会看到它仍等于15。它没有变为20. c.val_当然已改为20。

查看getA内部,您会看到(((A)*this) = 20)。让我们打破这个:

this     // a pointer to the the variable 'b' in main(). It's of type B*
*this    // a reference to 'b'. Of type B&
(A)*this // this copies into a new object of type A.

值得暂停。如果这是(A&)*this,甚至是*((A*)this),那么这将是一个更简单的路线。但它是(A)*this,因此这会创建一个A类型的新对象,并将相关切片从b复制到其中。

(额外:您可能会问它如何复制切片。我们有B&引用,我们希望创建一个新的A。默认情况下,编译器创建一个复制构造函数{{ 1}}。编译器可以使用它,因为引用A :: A (const A&)可以自然地转换为B&。)

特别是const A&。这可能会让你大吃一惊。 (额外:另一方面this != &((A)*this)通常(取决于是否有this == &((A&)*this)方法))

现在我们有了这个新对象,我们可以看看

virtual

这会将数字放入此新值中。此语句影响((A)*this) = 20

更改this->val_使其返回getA会出错。首先,A&的返回值为operator=,因此您无法将其作为const A&返回。但即使你有A&作为返回类型,这也是对getA中创建的临时局部变量的引用。回归这些事情是不明确的。

最后,我们可以看到const A&将采用getA的值返回的此副本

c

这就是为什么当前代码,其中getA按值返回副本,是安全且定义良好的。

==完整的程序==

A c = b.getA();