C ++ 11에는 C # 스타일 속성이 있습니까?


93

C #에는 getter 및 setter가있는 필드에 대한 멋진 구문 설탕이 있습니다. 또한 자동 구현 속성이 마음에 듭니다.

public Foo foo { get; private set; }

C ++에서 나는 작성해야

private:
    Foo foo;
public:
    Foo getFoo() { return foo; }

C ++ 11에 이러한 개념이있어 이에 대한 구문 설탕을 가질 수 있습니까?


62
몇 개의 매크로로 수행 할 수 있습니다. 수치심에 도망
치다

7
@Eloff : 모든 것을 공개하는 것은 항상 나쁜 생각입니다.
Kaiserludi 2014 년

8
그런 개념이 없습니다! 그리고 당신도 필요하지 않습니다 seanmiddleditch.com/why-c-does-not-need-c-like-properties을
CinCout

2
a)이 질문은 꽤 오래된 질문입니다. b) 구문 설탕을 요청했습니다. 괄호를 제거 할 수 있습니다. c)이 기사는 C ++ 속성이 '필요하거나 필요하지 않은'속성을 적용하는 것에 대한 유효한 인수를 제시하지만 매우 주관적입니다. C ++는 그것들이 없어도 Touring-machine과 동등하지만, 그러한 구문 설탕이 C ++의 생산성을 높일 수 있다는 것을 의미하지는 않습니다.
Radim Vansa

3
절대 아니다.

답변:


87

C ++에서는 자신 만의 기능을 작성할 수 있습니다. 다음은 명명되지 않은 클래스를 사용하는 속성 구현의 예입니다. Wikipedia 기사

struct Foo
{
    class {
        int value;
        public:
            int & operator = (const int &i) { return value = i; }
            operator int () const { return value; }
    } alpha;

    class {
        float value;
        public:
            float & operator = (const float &f) { return value = f; }
            operator float () const { return value; }
    } bravo;
};

자체 getter 및 setter를 제자리에 작성할 수 있으며 홀더 클래스 멤버 액세스를 원하는 경우이 예제 코드를 확장 할 수 있습니다.


1
이 코드를 수정하여 Foo가 내부적으로 액세스 할 수있는 개인 멤버 변수를 갖도록 수정하는 방법에 대한 아이디어가있는 반면 공용 API는 속성 만 노출합니까? 물론 Foo를 알파 / 베타의 친구로 만들 수는 있지만 값에 액세스하려면 여전히 alpha.value를 작성해야하지만 Foo 내부에서 멤버 변수에 직접 액세스하는 것이 더 좋을 것 같으면 더 선호합니다. 특수 중첩 속성 클래스의 멤버가 아닌 Foo 자체의 멤버에 액세스합니다.
Kaiserludi 2014 년

1
@Kaiserludi 예 :이 경우 알파와 브라보를 비공개로 설정합니다. Foo에서는 위의 "속성"으로 읽고 쓸 수 있지만 Foo 외부에서는 더 이상 불가능합니다. 이를 피하려면 public 인 const 참조를 만드십시오. 외부에서 접근 할 수 있지만, 상수 참조이므로 읽기 전용입니다. 유일한 경고는 public const 참조에 대해 다른 이름이 필요하다는 것입니다. 개인적으로 _alpha개인 변수와 alpha참조를 위해 사용합니다.
Kapichu

1
@Kapichu : 2 가지 이유 때문에 해결책이 아닙니다. 1) C # 속성에서 getter / setter는 클래스의 공용 사용자에게 강제로 적용되는 안전성 검사를 포함하는 데 자주 사용되며 멤버 함수는 값에 직접 액세스 할 수 있습니다. 2) const 참조는 무료가 아닙니다. 컴파일러 / 플랫폼에 따라 확장 sizeof(Foo)됩니다.
ceztko

@psx :이 접근 방식의 한계 때문에 나는 그것을 피하고 표준에 적절하게 추가 될 때까지 기다릴 것입니다.
ceztko 2015 년

@Kapichu :하지만 예제 코드의 alpha와 bravo는 속성입니다. 속성을 사용할 필요없이 Foo 구현 내부에서 변수 자체에 직접 액세스하고 싶은 반면, API의 속성을 통해서만 액세스를 노출하고 싶습니다.
Kaiserludi 2015-08-31

53

C ++에는이 기능이 내장되어 있지 않으므로 속성 기능을 모방 하는 템플릿정의 할 수 있습니다 .

template <typename T>
class Property {
public:
    virtual ~Property() {}  //C++11: use override and =default;
    virtual T& operator= (const T& f) { return value = f; }
    virtual const T& operator() () const { return value; }
    virtual explicit operator const T& () const { return value; }
    virtual T* operator->() { return &value; }
protected:
    T value;
};

속성정의 하려면 :

Property<float> x;

사용자 정의 getter / setter 를 구현하려면 상속하십시오.

class : public Property<float> {
    virtual float & operator = (const float &f) { /*custom code*/ return value = f; }
    virtual operator float const & () const { /*custom code*/ return value; }
} y;

읽기 전용 속성 을 정의하려면 :

template <typename T>
class ReadOnlyProperty {
public:
    virtual ~ReadOnlyProperty() {}
    virtual operator T const & () const { return value; }
protected:
    T value;
};

그리고 수업에서 사용하려면Owner :

class Owner {
public:
    class : public ReadOnlyProperty<float> { friend class Owner; } x;
    Owner() { x.value = 8; }
};

더 간결하게 만들기 위해 매크로 에서 위의 일부를 정의 할 수 있습니다.


이것이 제로 비용 기능으로 컴파일되는지 궁금합니다. 예를 들어 클래스 인스턴스의 각 데이터 멤버를 래핑하면 동일한 종류의 구조 패킹이 생성되는지 알 수 없습니다.
Dai

1
"custom getter / setter"로직은 람다 함수를 사용하여 구문 적으로 더 깔끔하게 만들 수 있습니다. 불행히도 C ++에서 실행 가능한 컨텍스트 외부에서 람다를 정의 할 수 없습니다 (아직!). 따라서 전 처리기 매크로를 사용하지 않고는 코드로 끝납니다. 안타깝게도 멍청한 게터 / 세터처럼 어리 석습니다.
Dai

2
마지막 예의 "class : ..."는 흥미롭고 다른 예에서는 누락되었습니다. 새로운 클래스 이름을 도입하지 않고 필요한 친구 선언을 생성합니다.
Hans Olsson

이 답변과 2010-Nov-19 답변의 큰 차이점은 사례별로 getter 또는 setter를 재정의 할 수 있다는 것입니다. 이렇게하면 입력이 범위 내에 있는지 확인하거나 변경 알림을 보내 이벤트 리스너를 변경하거나 중단 점을 걸 수 있습니다.
Eljay

(가) 있습니다 virtual입니다 아마 속성이 다형 적 사용을 할 가능성이 있기 때문에, 대부분의 사용 사례에 대한 불필요한.
Eljay

28

모든 플랫폼과 컴파일러에서 작동하는 C ++ 언어에는 없습니다.

그러나 크로스 플랫폼 호환성을 깨고 특정 컴파일러에 커밋하려는 경우 이러한 구문을 사용할 수 있습니다. 예를 들어 Microsoft Visual C ++에서 다음을 수행 할 수 있습니다.

// declspec_property.cpp  
struct S {  
   int i;  
   void putprop(int j) {   
      i = j;  
   }  

   int getprop() {  
      return i;  
   }  

   __declspec(property(get = getprop, put = putprop)) int the_prop;  
};  

int main() {  
   S s;  
   s.the_prop = 5;  
   return s.the_prop;  
}


18

당신은 전용 유형의 멤버를 가지고와 재정 의하여 어느 정도 getter 및 setter를 에뮬레이트 할 수 operator(type)operator=그것을 위해. 그것이 좋은 아이디어인지 여부는 또 다른 질문이며 +1Kerrek SB의 답변으로 내 의견을 표현할 것입니다. :)


이러한 유형으로 할당하거나 읽을 때 메서드 호출을 에뮬레이션 할 수 있지만 할당 작업을 호출하는 사람을 구분할 수는 없습니다 (필드 소유자가 아닌 경우 금지)-다른 액세스를 지정하여 수행하려는 작업 게터 및 세터 레벨.
Radim Vansa 2011

@Flavius ​​: friend필드 소유자에게 a 를 추가하면 됩니다.
kennytm 2011

17

지난 시간 동안 내가 모은 속성 클래스를 살펴볼 수 있습니다 : /codereview/7786/c11-feedback-on-my-approach-to-c-like-class-properties

다음과 같이 동작하는 속성을 가질 수 있습니다.

CTestClass myClass = CTestClass();

myClass.AspectRatio = 1.4;
myClass.Left = 20;
myClass.Right = 80;
myClass.AspectRatio = myClass.AspectRatio * (myClass.Right - myClass.Left);

그러나 이것이 사용자 정의 접근자를 허용하지만 내가 찾고 있던 공개 getter / private setter 기능이 없습니다.
Radim Vansa 2012 년

17

C ++ 11을 사용하면 Property 클래스 템플릿을 정의하고 다음과 같이 사용할 수 있습니다.

class Test{
public:
  Property<int, Test> Number{this,&Test::setNumber,&Test::getNumber};

private:
  int itsNumber;

  void setNumber(int theNumber)
    { itsNumber = theNumber; }

  int getNumber() const
    { return itsNumber; }
};

그리고 여기에 Property 클래스 템플릿이 있습니다.

template<typename T, typename C>
class Property{
public:
  using SetterType = void (C::*)(T);
  using GetterType = T (C::*)() const;

  Property(C* theObject, SetterType theSetter, GetterType theGetter)
   :itsObject(theObject),
    itsSetter(theSetter),
    itsGetter(theGetter)
    { }

  operator T() const
    { return (itsObject->*itsGetter)(); }

  C& operator = (T theValue) {
    (itsObject->*itsSetter)(theValue);
    return *itsObject;
  }

private:
  C* const itsObject;
  SetterType const itsSetter;
  GetterType const itsGetter;
};

2
무슨 C::*뜻이야? 나는 전에 그런 것을 본 적이 없습니까?
Rika

1
class의 비 정적 멤버 함수에 대한 포인터 C입니다. 이것은 일반 함수 포인터와 유사하지만 멤버 함수를 호출하려면 함수가 호출되는 객체를 제공해야합니다. 이것은 itsObject->*itsSetter(theValue)위의 예 에서 라인 을 통해 이루어집니다 . 이 기능에 대한 자세한 설명은 여기 를 참조 하십시오 .
Christoph Böhme

@Niceman, 사용 예가 있습니까? 회원으로 가입하는 것은 비용이 많이 드는 것 같습니다. 정적 멤버로는 특히 유용하지 않습니다.
Grim Fandango

16

다른 많은 사람들이 이미 말했듯이 언어에 내장 된 지원이 없습니다. 그러나 Microsoft C ++ 컴파일러를 대상으로하는 경우 문서화 된 속성에 대한 Microsoft 특정 확장을 활용할 수 있습니다. 여기 .

다음은 링크 된 페이지의 예입니다.

// declspec_property.cpp
struct S {
   int i;
   void putprop(int j) { 
      i = j;
   }

   int getprop() {
      return i;
   }

   __declspec(property(get = getprop, put = putprop)) int the_prop;
};

int main() {
   S s;
   s.the_prop = 5;
   return s.the_prop;
}

12

아니요, C ++에는 속성 개념이 없습니다. getThis () 또는 setThat (value)를 정의하고 호출하는 것이 어색 할 수 있지만 이러한 메서드의 소비자에게 일부 기능이 발생할 수 있다는 설명을하고 있습니다. 반면에 C ++에서 필드에 액세스하면 추가 또는 예상치 못한 기능이 발생하지 않는다는 것을 소비자에게 알립니다. 속성 액세스는 언뜻보기에 필드처럼 반응하는 것처럼 보이지만 실제로는 메서드처럼 반응하므로 속성은이를 덜 명확하게 만듭니다.

제쳐두고 저는 고객 멤버십 시스템을 만들려고 시도하는 .NET 응용 프로그램 (아주 잘 알려진 CMS)에서 작업하고있었습니다. 사용자 개체에 대한 속성을 사용하는 방식으로 인해 예상치 못한 작업이 실행되어 무한 재귀를 포함하여 기이 한 방식으로 구현이 실행되었습니다. 이는 사용자 개체가 StreetAddress와 같은 간단한 항목에 액세스하려고 할 때 데이터 액세스 레이어 또는 일부 글로벌 캐싱 시스템을 호출했기 때문입니다. 그들의 전체 시스템은 내가 재산 남용이라고 부르는 것에 기반을두고 있습니다. 속성 대신 메서드를 사용했다면 무엇이 잘못되었는지 훨씬 더 빨리 파악했을 것입니다. 그들이 필드를 사용했다면 (또는 적어도 그들의 속성이 필드처럼 동작하도록 만들었 더라면) 시스템을 확장하고 유지하는 것이 더 쉬웠을 것입니다.

[편집] 내 생각을 바꿨습니다. 나는 나쁜 하루를 보냈고 약간의 호언을했다. 이 정리는 더 전문적이어야합니다.


11

https://stackoverflow.com/a/23109533/404734를 기반으로 여기에 공개 getter 및 private setter가있는 버전이 있습니다.

struct Foo
{
    class
    {
            int value;
            int& operator= (const int& i) { return value = i; }
            friend struct Foo;
        public:
            operator int() const { return value; }
    } alpha;
};

4

이것은 정확히 속성은 아니지만 간단한 방법으로 원하는 것을 수행합니다.

class Foo {
  int x;
public:
  const int& X;
  Foo() : X(x) {
    ...
  }
};

여기서 큰 X public int X { get; private set; }는 C # 구문 에서처럼 동작 합니다. 본격적인 속성을 원한다면 여기 에서 구현하는 첫 번째 샷을 만들었습니다 .


2
이것은 좋은 생각이 아닙니다. 이 클래스의 개체를 복사 할 때마다 X새 개체 의 참조 는 포인터 멤버처럼 복사되기 때문에 이전 개체의 멤버를 계속 가리 킵니다. 이것은 그 자체로 나쁘지만 오래된 개체가 삭제되면 그 위에 메모리 손상이 발생합니다. 이 작업을 수행하려면 자체 복사 생성자, 할당 연산자 및 이동 생성자를 구현해야합니다.
toster

4

당신은 아마 그것을 알고있을 것입니다. 그러나 저는 단순히 다음과 같이 할 것입니다.

class Person {
public:
    std::string name() {
        return _name;
    }
    void name(std::string value) {
        _name = value;
    }
private:
    std::string _name;
};

이 접근 방식은 간단하고 영리한 트릭을 사용하지 않으며 작업을 완료합니다!

문제는 일부 사람들이 자신의 개인 필드 앞에 밑줄을 붙이는 것을 좋아하지 않아서이 접근 방식을 실제로 사용할 수 없다는 것입니다.하지만 다행히도 그렇게하는 사람들에게는 정말 간단합니다. :)

get 및 set 접두사는 API에 명확성을 추가하지 않지만 더 장황하게 만들고 유용한 정보를 추가하지 않는다고 생각하는 이유는 API가 합리적이면 누군가 API를 사용해야 할 때 API가 무엇인지 알 수 있기 때문입니다. 접두사없이합니다.

한 가지 더, 이것들이 속성이라는 것을 이해하기 쉽습니다. name 동사가 아니기 .

최악의 시나리오는 API가 일관되고 사용자가 그것이 name()접근 자이고name(value) 뮤 테이터 , 패턴을 이해하기 위해 문서에서 한 번만 조회하면됩니다.

C #을 좋아하는만큼 C ++에 속성이 전혀 필요하지 않다고 생각합니다!


뮤 테이터가 foo(bar)(느린 대신) 사용하면 의미가 foo = bar있지만 접근자는 속성과 전혀 관련이 없습니다 ...
Matthias

@Matthias 그것은 속성과 전혀 관련이 없다고 말하면서 나에게 아무것도 말하지 않습니다. 게다가 나는 그것들을 비교하지 않았지만 mutator와 accessor가 필요하다면이 규칙을 사용할 수 있습니다.
Eyal Solnik

질문은 속성의 소프트웨어 개념에 관한 것입니다. 속성은 공용 데이터 멤버 인 것처럼 사용할 수 있지만 (사용) 실제로는 접근 자 (선언)라는 특수 메서드입니다. 솔루션은 선언 및 사용법에 대한 메서드 (일반적인 게터 / 세터)를 고수합니다. 그래서 이것은 무엇보다도 OP가 요구하는 사용법이 아니라 다소 이상하고 비 전통적인 명명 규칙입니다 (따라서 두 번째로 구문 설탕도 없음).
Matthias

사소한 부작용으로 뮤 테이터는 놀랍게도 속성으로 작동합니다. C ++ 에서는 멤버 변수 의 뮤 테이터 메서드를 foo(bar)사용 하여 대신 초기화 foo=bar할 수 있기 때문입니다 . void foo(Bar bar)_foo
Matthias

@Matthias 저는 속성이 무엇인지 알고 있습니다. 저는 10 년 넘게 C ++과 C #을 작성해 왔습니다. 저는 속성의 이점과 그 속성에 대해 논쟁하지 않지만 C ++에서 실제로 필요하지 않았습니다. '공용 데이터로 사용할 수 있으며 대부분 사실이지만 C #에서는 속성을 ref로 전달하는 것과 같이 속성을 직접 사용할 수없는 경우가 있지만 public 필드에서는 사용할 수 있습니다.
Eyal Solnik

4

아니요.하지만 그냥 get : set 함수이고 get : set 메서드 내부에 추가 작업이 수행되지 않았는지 여부를 고려해야합니다.


2

여러 C ++ 소스에서 아이디어를 수집하여 C ++의 getter / setter에 대한 훌륭하고 여전히 매우 간단한 예제에 넣었습니다.

class Canvas { public:
    void resize() {
        cout << "resize to " << width << " " << height << endl;
    }

    Canvas(int w, int h) : width(*this), height(*this) {
        cout << "new canvas " << w << " " << h << endl;
        width.value = w;
        height.value = h;
    }

    class Width { public:
        Canvas& canvas;
        int value;
        Width(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } width;

    class Height { public:
        Canvas& canvas;
        int value;
        Height(Canvas& canvas): canvas(canvas) {}
        int & operator = (const int &i) {
            value = i;
            canvas.resize();
            return value;
        }
        operator int () const {
            return value;
        }
    } height;
};

int main() {
    Canvas canvas(256, 256);
    canvas.width = 128;
    canvas.height = 64;
}

산출:

new canvas 256 256
resize to 128 256
resize to 128 64

여기에서 온라인으로 테스트 할 수 있습니다. http://codepad.org/zosxqjTX


자기 굴절을 유지하기위한 메모리 오버 헤드, + akward ctor 구문이 있습니다.
Red.Wave

부동산을 제안하려면? 거절당한 제안이 많이 있었나 봐요.
Red.Wave

@ Red.Wave 거절의 주인과 주인에게 인사하십시오. C ++에 오신 것을 환영합니다. 자체 참조를 원하지 않는 경우 Clang 및 MSVC에는 속성에 대한 사용자 지정 확장이 있습니다.
lama12345

나는 절할 수 없습니다. 모든 기능이 적절한 것은 아닙니다. 나에게 객체는 setter + getter 함수 쌍 이상입니다. 필자는 불필요한 영구 메모리 오버 헤드를 피하기 위해 자체 구현을 시도했지만 인스턴스 선언의 구문과 작업은 만족스럽지 않았습니다. 나는 선언적 매크로를 사용하는 것에 끌 렸지만 어쨌든 매크로를 좋아하지는 않습니다. 그리고 마침내 저의 접근 방식은 저를 포함한 많은 사람들이 승인하지 않는 함수 구문으로 액세스되는 속성으로 이어졌습니다.
Red.Wave

0

클래스가 실제로 일부 불변성을 적용해야합니까, 아니면 구성원 요소의 논리적 그룹입니까? 후자의 경우에는 그 일을 구조체로 만들고 멤버에 직접 액세스하는 것을 고려해야합니다.


0

기록 된 매크로 세트가 있습니다 여기에 . THis에는 값 유형, 참조 유형, 읽기 전용 유형, 강력한 유형 및 약한 유형에 대한 편리한 속성 선언이 있습니다.

class MyClass {

 // Use assign for value types.
 NTPropertyAssign(int, StudentId)

 public:
 ...

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.