문제

특정 경우에 const가 어떻게 적용되는지 정확히 파악하는 데 약간의 문제가 있습니다.내가 가진 코드는 다음과 같습니다.

struct Widget
{
    Widget():x(0), y(0), z(0){}

    int x, y, z;
};

struct WidgetHolder //Just a simple struct to hold four Widgets.
{
    WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

    Widget& A;
    Widget& B;
    Widget& C;
    Widget& D;
};

class Test //This class uses four widgets internally, and must provide access to them externally.
{
    public:
        const WidgetHolder AccessWidgets() const
        {
            //This should return our four widgets, but I don't want anyone messing with them.
            return WidgetHolder(A, B, C, D);
        }

        WidgetHolder AccessWidgets()
        {
            //This should return our four widgets, I don't care if they get changed.
            return WidgetHolder(A, B, C, D);
        }

    private:
        Widget A, B, C, D;
};

int main()
{
    const Test unchangeable;

    unchangeable.AccessWidgets().A.x = 1; //Why does this compile, shouldn't the Widget& be const?
}

기본적으로 test라는 클래스가 있습니다.내부적으로 4개의 위젯을 사용하고 이를 반환하려면 위젯이 필요하지만 테스트가 const로 선언된 경우 위젯도 const를 반환하길 원합니다.

main()의 코드가 컴파일되는 이유를 누군가 설명해 줄 수 있나요?

매우 감사합니다.

도움이 되었습니까?

해결책

이는 WidgetHolder가 const 개체임에도 불구하고 이 const-ness가 WidgetHolder를 가리키는(참조되는) 개체에 자동으로 적용되지 않기 때문에 컴파일됩니다.기계 수준에서 생각해보세요. WidgetHolder 개체 자체가 읽기 전용 메모리에 보관되어 있으면 WidgetHolder가 가리키는 항목에 계속 쓸 수 있습니다.

문제는 다음 줄에 있는 것 같습니다.

WidgetHolder(Widget a, Widget b, Widget c, Widget d): A(a), B(b), C(c), D(d){}

Frank가 언급했듯이 WidgetHolder 클래스 내부의 참조는 생성자가 반환된 후 잘못된 참조를 보유하게 됩니다.따라서 이를 다음과 같이 변경해야 합니다.

WidgetHolder(Widget &a, Widget &b, Widget &c, Widget &d): A(a), B(b), C(c), D(d){}

그렇게 하면 컴파일되지 않으므로 독자가 나머지 솔루션을 해결할 수 있도록 연습용으로 남겨 둡니다.

다른 팁

const Widget& 객체를 보유하기 위해 특별히 새로운 유형을 생성해야 합니다.즉:


struct ConstWidgetHolder
{
    ConstWidgetHolder(const Widget &a, const Widget &b, const Widget &c, const Widget &d): A(a), B(b), C(c), D(d){}

    const Widget& A;
    const Widget& B;
    const Widget& C;
    const Widget& D;
};

class Test
{
public:
    ConstWidgetHolder AccessWidgets() const
    {
        return ConstWidgetHolder(A, B, C, D);
    }

이제 다음과 같은 오류가 발생합니다(gcc 4.3에서).

widget.cc: In function 'int main()':
widget.cc:51: error: assignment of data-member 'Widget::x' in read-only structure

유사한 관용구가 표준 라이브러리에서 반복자와 함께 사용됩니다. 즉:


class vector {
    iterator begin();
    const_iterator begin() const;

변경할 수 없습니다.액세스 위젯():

이 시점에서는 WidgetHolder 유형의 새 객체를 생성합니다.이 개체는 const로 보호되지 않습니다.

또한 Wdiget에 대한 참조가 아닌 WidgetHolder에 새 위젯을 생성합니다.

당신의 WidgetHolder 잘못된 참조(포인터)를 보유하게 됩니다.스택의 개체를 생성자에 전달한 다음 해당 (임시) 주소에 대한 참조를 보유합니다.이것은 깨지는 것이 보장됩니다.

참조 자체와 수명이 같거나 그 이상인 개체에만 참조를 할당해야 합니다.

참조를 보유해야 하는 경우 생성자에 참조를 전달합니다.더 좋은 점은 참조 자료를 전혀 보관하지 말고 복사본을 만드는 것입니다.

편집하다:그 사람이 답변을 삭제해서 제가 좀 바보처럼 보이더라고요 :)

Flame의 답변은 위험할 정도로 잘못되었습니다.그의 WidgetHolder는 생성자의 값 객체에 대한 참조를 취합니다.생성자가 반환되자마자 해당 값으로 전달된 개체는 삭제되므로 삭제된 개체에 대한 참조를 유지하게 됩니다.

그의 코드를 사용하는 매우 간단한 샘플 앱은 다음을 명확하게 보여줍니다.

#include <iostream>

class Widget
{
    int x;
public:
    Widget(int inX) : x(inX){}
    ~Widget() {
    std::cout << "widget " << static_cast< void*>(this) << " destroyed" << std::endl;
     }
};

struct WidgetHolder
{
    Widget& A;

public:
    WidgetHolder(Widget a): A(a) {}

    const Widget& a() const {
    std::cout << "widget " << static_cast< void*>(&A) << " used" << std::endl;
    return A;
}

};

int main(char** argv, int argc)
{
Widget test(7);
WidgetHolder  holder(test);
Widget const & test2 = holder.a();

return 0;
} 

출력은 다음과 같습니다

widget 0xbffff7f8 destroyed
widget 0xbffff7f8 used
widget 0xbffff7f4 destroyed

이를 방지하려면 WidgetHolder 생성자는 참조로 저장하려는 변수에 대한 참조를 가져와야 합니다.

struct WidgetHolder
{
    Widget& A;

public:
    WidgetHolder(Widget & a): A(a) {}

  /* ... */

};

원래 쿼리는 포함 클래스가 const인 경우 WidgetHolder를 const로 반환하는 방법이었습니다.C++에서는 함수 시그니처의 일부로 const를 사용하므로 동일한 함수에 대해 const 버전이 있고 const 버전이 없을 수 있습니다.none const는 인스턴스가 const가 아닐 때 호출되고, const는 인스턴스가 const일 때 호출됩니다.따라서 해결책은 직접 접근하기보다는 기능을 통해 위젯 홀더의 위젯에 접근하는 것입니다.나는 원래 질문에 대한 답이라고 생각하는 더 간단한 예를 아래에 만들었습니다.

#include <stdio.h>

class Test
{
public:
  Test(int v){m_v = v;}
 ~Test(){printf("Destruct value = %d\n",m_v);}

 int& GetV(){printf ("None Const returning %d\n",m_v); return m_v;  }

 const int& GetV() const { printf("Const returning %d\n",m_v); return m_v;}
private:
  int m_v;
};

void main()
{
  // A none const object (or reference) calls the none const functions
  // in preference to the const
  Test one(10);
  int& x = one.GetV();
  // We can change the member variable via the reference
  x = 12;

  const Test two(20);
  // This will call the const version  
  two.GetV();

  // So the below line will not compile
  // int& xx = two.GetV();

  // Where as this will compile
  const int& xx = two.GetV();

  // And then the below line will not compile
  // xx = 3;

}

원래 코드의 관점에서 보면 WidgetHolder를 Test 클래스의 멤버로 갖고 이에 대한 const 참조를 반환하거나 const 참조를 반환하지 않고 Widgets를 홀더의 비공개 멤버로 만들고 다음을 제공하는 것이 더 쉬울 것이라고 생각합니다. const이며 각 위젯에 대한 const 접근자는 없습니다.

class WidgetHolder {
...

Widget& GetA();
const Widget& GetA() const;
...
};

그리고 메인 수업에서는

class Test {
...
WigetHolder& AccessWidgets() { return m_Widgets;}
const WidgetHolder&AcessWidgets() const { return m_Widgets;}

private:
  WidgetHolder m_Widgets;
...
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top