修改常量对象

6

我正在查看一份初级C++开发人员职位的面试问题。问题是(引用):

以下代码是否正确

struct Foo
{
    int i;
    void foo ( void ) const
    {
        Foo* pointer = const_cast<Foo*>(this);
        pointer->i = 0;
    }
};

我会这样回答:

根据C++03和c++11标准,代码本身是有效的,可以成功编译。

但是,如果调用 foo() 的类实例被声明为 const,则在赋值 pointer->i = 0; 期间可能会引发未定义的行为。

我的意思是,以下代码将成功编译并导致未定义的行为。

struct Foo
{
    int i;
    Foo ( void )
    {

    }
    void foo ( void ) const
    {
        Foo* pointer = const_cast<Foo*>(this);
        pointer->i = 0;
    }
};

int main ( void )
{
    Foo foo1;
    foo1.foo();   // Ok

    const Foo foo2;
    foo2.foo();   // UB

    return 0;
}

我的回答是否正确或者我有遗漏之处?谢谢。

11
是的,这是正确的。 - R. Martinho Fernandes
3
“Correct”这个词相当模糊。它是“well-formed”的意思,也就是说它没有可诊断的错误,但是(正如你所说),它可能会导致未定义的行为。 - Mike Seymour
从设计的角度来看,我认为这并不是“正确”的,因为 const Foo foo; foo.foo(); 不应该是未定义行为。使用一个 mutable 成员会更好。 - StackedCrooked
@MatsPetersson 我同意,这些是相关的问题。我只是想确保我理解得完全正确。我应该删除这个问题吗? - Kolyunya
如果你已经通过了第一个问题,那么接下来的问题是:请举一个const_cast既安全又实用的使用示例,而不是一个玩具示例。 - Yakk - Adam Nevraumont
3个回答

5
我首先会问他们所谓的“正确”是什么意思。您需要了解程序的规范及其预期行为。
就像您所说的,如果他们只是询问它是否可以编译,那么答案是“是”。但是,如果他们认为“正确”的含义是安全执行,那么您可以讨论您在问题中提到的事实。
通过这种回答方式,面试者可以看到您正在分析被问到的内容,而不是直接回答,我认为这是一件好事。

1
这段代码可能在法律上是正确的,但我猜测问题的目的是确定您是否理解const本身的概念。因为在语义层面上,您有一个函数接受一个隐式const对象指针,然后对其进行修改,这几乎肯定是一个错误。
有些情况下,这可能是所需的(因为修改是一些返回值缓存或类似操作,不会改变对象的语义值),您可以使用mutable关键字来处理相关变量。

谢谢。在答案中提到将i声明为mutable可以修复可能的UB,这是一个好主意。 - Kolyunya
去除constness是设计中存在缺陷的迹象。然后,您开始分析其后果。 - SChepurin

0

作弊编译器将承担后果。

struct Foo
{
    int i;

    Foo(int a):i(a){}

    void foo ( void ) const
    {
        Foo* pointer = const_cast<Foo*>(this);
        pointer->i = 0;
    }

    bool operator<(const Foo& rhs) const{
        return i<rhs.i;
    }
};

#include <map>

int main ( void )
{
    std::map<Foo,int> kk;

    for(int i=0;i<10;++i){
        kk.insert(std::make_pair(Foo(i),i));
    }

    std::map<Foo,int>::iterator ite = kk.find(Foo(4));
    const Foo& foo4 = ite->first;
    foo4.foo();

    ite = kk.find(Foo(4));
    const Foo& tmp = ite->first; // crack

    return 0;
}

程序将在“const Foo& tmp = ite->first;”处崩溃。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接