C ++ 게터 / 세터 코딩 스타일
나는 한동안 C #으로 프로그래밍을 해왔고 지금은 C ++ 기술을 연마하고 싶습니다.
수업 :
class Foo
{
const std::string& name_;
...
};
가장 좋은 방법은 무엇입니까 (name_ 필드에 대한 읽기 액세스 만 허용하고 싶습니다).
- getter 메소드를 사용하십시오.
inline const std::string& name() const { return name_; } - 그것은 상수이기 때문에 필드를 공개합니다
감사.
non-const 필드를 공개하는 것은 잘못된 생각 인 경향이 있습니다. 그러면 오류 검사 제약 조건을 강제하거나 향후 값 변경에 부작용을 추가하기가 어려워지기 때문입니다.
귀하의 경우에는 const 필드가 있으므로 위의 문제는 문제가되지 않습니다. 공개 필드로 만드는 주된 단점은 기본 구현을 잠그고 있다는 것입니다. 예를 들어 앞으로 내부 표현을 C- 문자열이나 유니 코드 문자열 또는 다른 것으로 변경하려는 경우 모든 클라이언트 코드를 손상시킬 수 있습니다. 게터를 사용하면 새 게터를 통해 새 사용자에게 새 기능을 제공하는 동시에 기존 클라이언트의 레거시 표현으로 변환 할 수 있습니다.
위에 배치 한 것과 같은 getter 메서드를 사용하는 것이 좋습니다. 이것은 미래의 유연성을 극대화 할 것입니다.
getter 메서드를 사용하면 나중에 getter 메서드를 더 복잡한 것으로 대체 할 수 있으므로 수명이 긴 클래스에 대한 더 나은 디자인 선택입니다. 이것이 const 값에 필요한 것 같지는 않지만 비용이 낮고 가능한 이점이 큽니다.
제쳐두고, C ++에서는 멤버 의 getter와 setter에 같은 이름을 지정 하는 것이 특히 좋은 생각 입니다. 나중에 실제로 메서드 쌍을 변경할 수 있기 때문입니다.
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 만 필요한 const 값에 대해서도 잘 작동합니다.
제쳐두고, C ++에서 const 참조 멤버를 갖는 것은 다소 이상합니다. 생성자 목록에서 할당해야합니다. 그 객체의 실제 메모리는 누가 소유하고 수명은 얼마입니까?
스타일에 관해서는, 나는 당신이 당신의 사생활을 드러내고 싶지 않다는 다른 사람들의 의견에 동의합니다. :-) 나는 세터 / 게터를위한이 패턴을 좋아합니다
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;
}
Visual Studio 2013에서 테스트했습니다. 안타깝게도 LambdaSetter 내부의 기본 스토리지를 사용하기 위해 캡슐화가 깨질 수있는 "GetRawData"퍼블릭 접근자를 제공해야했지만,이를 제외하고 자체 스토리지 컨테이너를 제공 할 수 있습니다. T 또는 "GetRawData"를 사용하는 유일한 시간이 사용자 지정 getter / setter 메서드를 작성할 때인 지 확인하십시오.
Even though the name is immutable, you may still want to have the option of computing it rather than storing it in a field. (I realize this is unlikely for "name", but let's aim for the general case.) For that reason, even constant fields are best wrapped inside of getters:
class Foo {
public:
const std::string& getName() const {return name_;}
private:
const std::string& name_;
};
Note that if you were to change getName() to return a computed value, it couldn't return const ref. That's ok, because it won't require any changes to the callers (modulo recompilation.)
Avoid public variables, except for classes that are essentially C-style structs. It's just not a good practice to get into.
Once you've defined the class interface, you might never be able to change it (other than adding to it), because people will build on it and rely on it. Making a variable public means that you need to have that variable, and you need to make sure it has what the user needs.
Now, if you use a getter, you're promising to supply some information, which is currently kept in that variable. If the situation changes, and you'd rather not maintain that variable all the time, you can change the access. If the requirements change (and I've seen some pretty odd requirements changes), and you mostly need the name that's in this variable but sometimes the one in that variable, you can just change the getter. If you made the variable public, you'd be stuck with it.
This won't always happen, but I find it a lot easier just to write a quick getter than to analyze the situation to see if I'd regret making the variable public (and risk being wrong later).
Making member variables private is a good habit to get into. Any shop that has code standards is probably going to forbid making the occasional member variable public, and any shop with code reviews is likely to criticize you for it.
Whenever it really doesn't matter for ease of writing, get into the safer habit.
Collected ideas from multiple C++ sources and put it into a nice, still quite simple example for getters/setters in C++:
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;
}
Output:
new canvas 256 256
resize to 128 256
resize to 128 64
You can test it online here: http://codepad.org/zosxqjTX
PS: FO Yvette <3
From the Design Patterns theory; "encapsulate what varies". By defining a 'getter' there is good adherence to the above principle. So, if the implementation-representation of the member changes in future, the member can be 'massaged' before returning from the 'getter'; implying no code refactoring at the client side where the 'getter' call is made.
Regards,
참고URL : https://stackoverflow.com/questions/760777/c-getters-setters-coding-style
'Program Club' 카테고리의 다른 글
| 매우 긴 문자열 목록에 대한 적절한 검색 / 검색 방법은 무엇입니까? (0) | 2020.11.16 |
|---|---|
| 데이터베이스가없는 Rails 모델 (0) | 2020.11.16 |
| 루비에서 문자열을 숫자와 연결 (0) | 2020.11.16 |
| NSString을 설정된 길이로 자르려면 어떻게해야합니까? (0) | 2020.11.16 |
| Ruby-문자열에서 일부 문자를 선택하는 방법 (0) | 2020.11.16 |