문제

나는 한동안 C#로 프로그래밍 해 왔으며 이제 C ++ 기술을 닦고 싶습니다.

수업 :

class Foo
{
    const std::string& name_;
    ...
};

최상의 접근 방식은 무엇입니까 (이름 _ 필드에 대한 읽기 액세스 만 허용하고 싶습니다) :

  • getter 메소드 사용 : inline const std::string& name() const { return name_; }
  • 일정하기 때문에 현장을 공개하십시오

감사.

도움이 되었습니까?

해결책

비 정통 필드를 공개하는 것은 나쁜 생각 인 경향이 있습니다. 왜냐하면 오류 확인 제약 조건을 강제하기가 어려워 지거나 미래의 가치 변화에 부작용을 추가하기 때문입니다.

귀하의 경우에는 Const 필드가 있으므로 위의 문제는 문제가되지 않습니다. 공개 분야로 만드는 주요 단점은 기본 구현을 잠그고 있다는 것입니다. 예를 들어, 미래에 내부 표현을 C- 스트링 또는 유니 코드 문자열 또는 다른 것으로 변경하려면 모든 클라이언트 코드를 중단합니다. Getter를 사용하면 기존 클라이언트의 레거시 표현으로 전환하면서 새로운 Getter를 통해 새로운 사용자에게 새로운 기능을 제공 할 수 있습니다.

나는 여전히 당신이 위에 놓은 것과 같은 getter 방법을 갖는 것이 좋습니다. 이것은 미래의 유연성을 극대화 할 것입니다.

다른 팁

getter 방법을 사용하는 것은 장기 클래스를위한 더 나은 디자인 선택입니다. 이는 Getter 방법을 미래에 더 복잡한 것으로 바꿀 수 있습니다. 이것은 Const 값에 필요할 가능성이 적지 만 비용은 낮으며 가능한 이점은 큽니다.

제쳐두고 C ++에서는 회원에게 게터와 세터를 모두주는 것이 특히 좋습니다. 같은 이름, 앞으로는 실제로 한 쌍의 방법을 변경할 수 있습니다.

class Foo {
public:
    std::string const& name() const;          // Getter
    void name(std::string const& newName);    // Setter
    ...
};

정의하는 단일 공개 회원 변수로 operator()() 각각:

// This class encapsulates a fancier type of name
class fancy_name {
public:
    // Getter
    std::string const& operator()() const {
        return _compute_fancy_name();    // Does some internal work
    }

    // Setter
    void operator()(std::string const& newName) {
        _set_fancy_name(newName);        // Does some internal work
    }
    ...
};

class Foo {
public:
    fancy_name name;
    ...
};

클라이언트 코드는 물론 다시 컴파일해야하지만 구문 변경이 필요하지 않습니다! 분명히,이 변환은 getter 만 필요합니다.

제쳐두고 C ++에서는 Const Reference 멤버를 갖는 것이 다소 이상합니다. 생성자 목록에 할당해야합니다. 누가 그 대상의 기억을 소유하고 있으며 평생은 무엇입니까?

스타일에 관해서는, 나는 당신이 당신의 개인을 폭로하고 싶지 않은 다른 사람들과 동의합니다. :-) 나는 세터/getters에 대한이 패턴을 좋아합니다

class Foo
{
public:
  const string& FirstName() const;
  Foo& FirstName(const string& newFirstName);

  const string& LastName() const;
  Foo& LastName(const string& newLastName);

  const string& Title() const;
  Foo& Title(const string& newTitle);
};

이렇게하면 다음과 같은 작업을 수행 할 수 있습니다.

Foo f;
f.FirstName("Jim").LastName("Bob").Title("Programmer");

나는 C ++ 11 접근법이 지금 이와 같을 것이라고 생각합니다.

#include <string>
#include <iostream>
#include <functional>

template<typename T>
class LambdaSetter {
public:
    LambdaSetter() :
        getter([&]() -> T { return m_value; }),
        setter([&](T value) { m_value = value; }),
        m_value()
    {}

    T operator()() { return getter(); }
    void operator()(T value) { setter(value); }

    LambdaSetter operator=(T rhs)
    {
        setter(rhs);
        return *this;
    }

    T operator=(LambdaSetter rhs)
    {
        return rhs.getter();
    }

    operator T()
    { 
        return getter();
    }


    void SetGetter(std::function<T()> func) { getter = func; }
    void SetSetter(std::function<void(T)> func) { setter = func; }

    T& GetRawData() { return m_value; }

private:
    T m_value;
    std::function<const T()> getter;
    std::function<void(T)> setter;

    template <typename TT>
    friend std::ostream & operator<<(std::ostream &os, const LambdaSetter<TT>& p);

    template <typename TT>
    friend std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p);
};

template <typename T>
std::ostream & operator<<(std::ostream &os, const LambdaSetter<T>& p)
{
    os << p.getter();
    return os;
}

template <typename TT>
std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p)
{
    TT value;
    is >> value;
    p.setter(value);
    return is;
}


class foo {
public:
    foo()
    {
        myString.SetGetter([&]() -> std::string { 
            myString.GetRawData() = "Hello";
            return myString.GetRawData();
        });
        myString2.SetSetter([&](std::string value) -> void { 
            myString2.GetRawData() = (value + "!"); 
        });
    }


    LambdaSetter<std::string> myString;
    LambdaSetter<std::string> myString2;
};

int _tmain(int argc, _TCHAR* argv[])
{
    foo f;
    std::string hi = f.myString;

    f.myString2 = "world";

    std::cout << hi << " " << f.myString2 << std::endl;

    std::cin >> f.myString2;

    std::cout << hi << " " << f.myString2 << std::endl;

    return 0;
}

불행히도 Lambdasetter 내부의 기본 스토리지를 사용하기 위해서는 캡슐화가 깨질 수있는 "GetRawdata"공개 액세서를 제공해야했지만 퇴치하고 자신의 스토리지 컨테이너를 제공 할 수 있습니다. t 또는 "getrawdata"를 사용하는 유일한 시간은 사용자 정의 getter/setter 메소드를 작성할 때입니다.

이름은 불변이지만 여전히 필드에 저장하는 대신 컴퓨팅을하는 옵션을 원할 수 있습니다. (나는 이것이 "이름"에 적합하지 않지만 일반적인 경우를 목표로합시다.) 그런 이유로, 일정한 필드조차도 Getters 안에 가장 잘 포장된다.

class Foo {
    public:
        const std::string& getName() const {return name_;}
    private:
        const std::string& name_;
};

당신이 변화한다면 getName() 계산 된 값을 반환하려면 Const Ref를 반환 할 수 없습니다. 발신자의 변경이 필요하지 않기 때문에 괜찮습니다 (모듈로 재 컴파일).

본질적으로 C 스타일 스트러크 인 클래스를 제외하고는 공개 변수를 피하십시오. 들어가는 것은 좋은 관행이 아닙니다.

클래스 인터페이스를 정의한 후에는 클래스 인터페이스를 변경할 수 없을 수도 있습니다 (추가하는 것 외에는)가 사람들이 그것을 구축하고 의존하기 때문입니다. 가변적 인 공개는 해당 변수가 있어야하며 사용자에게 필요한 것이 있는지 확인해야합니다.

이제 Getter를 사용하는 경우 현재 해당 변수에 보관되어있는 정보를 제공 할 것을 약속합니다. 상황이 변경되고 항상 해당 변수를 유지하지 않으려면 액세스를 변경할 수 있습니다. 요구 사항이 변경되면 (그리고 꽤 이상한 요구 사항이 변경된 경우), 대부분이 변수에있는 이름이 필요하지만 때로는 해당 변수의 이름이 필요합니다. 가변을 공개했다면, 당신은 그것에 붙어있을 것입니다.

이런 일이 항상 발생하지는 않지만 상황을 분석하여 가변적 인 대중을 공개하는 것을 후회하는지 확인하는 것보다 빠른 getter를 작성하는 것이 훨씬 쉽다는 것을 알게되었습니다.

회원 변수를 비공개로 만드는 것은 좋은 습관입니다. 코드 표준이있는 상점은 아마도 가끔 회원 가변을 공개적으로 공개하는 것을 금지 할 것이며, 코드 리뷰가있는 상점은 당신을 비판 할 것입니다.

글쓰기의 편의가 중요하지 않을 때마다 더 안전한 습관에 빠지십시오.

여러 C ++ 소스에서 아이디어를 수집하여 C ++의 Getters/Setters를위한 멋지고 여전히 간단한 예제에 넣습니다.

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

추신 : fo yvette <3

디자인 패턴 이론에서; "무엇이 변하는지 캡슐화하십시오". 'getter'를 정의함으로써 위의 원리를 잘 준수합니다. 따라서 회원의 구현이 향후 변경되면 'getter'에서 돌아 오기 전에 멤버를 '마사지'할 수 있습니다. 'getter'호출이 이루어지는 클라이언트 측에서 코드 리팩토링을 암시하지 않습니다.

문안 인사,

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