"앞으로 깨지지 않는"접근 템플릿 클래스[C++]
-
19-09-2019 - |
문제
지 않는 한 나는 철저하게 잘못이터 구 패턴은 일반적인 패턴은 두 가지를 사용:
- 비공개 변수는 사용할 수 있습니다,하지만 결코 수정에 의해서만 제공하는
getVariable
방법(또는지만 수정할 수 있으로만 제공setVariable
method). - 는지 확인하는,미래에 일어날 경우에 문제가있는 좋은 해결책이 될 것이 단순히 치료하는 변수에 들어가기 전에 그리고/또는 클래스에,당신은 당신을 치료할 수 있는 변수를 사용하여 실제 구현에 getter 및 setter 방법 대신 자이나 설정값입니다.는 방법,변경이 적용되지 않 나머지 부분의 코드입니다.
질문#1:나는 모든 사용의 접근 또는 내 가정을 반드시 보장되지는 않습니다나는 확실하지 않다면 내가 올바른 이들에.
질문#2:는 거의 모든 종류의 템플릿을 선을 유지할 수 있는 나에게서를 작성 접근을 위한 내 구성원 변수가?나를 찾을 수 없습니다.
질문#3:것 다음과 같은 템플릿 클래스의 좋은 방법이 될 구현 getter 하지 않고도 실제로 쓰기 accesor?
template <class T>
struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
{
typedef T Type;
};
template <typename T,class Owner>
class Getter
{
public:
friend class TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
template <typename ... Args>
Getter(Args args) : value(args ...) {} // Uses C++0x
T get() { return value; }
protected:
T value;
};
class Window
{
public:
Getter<uint32_t,Window> width;
Getter<uint32_t,Window> height;
void resize(uint32_t width,uint32_t height)
{
// do actual window resizing logic
width.value = width; // access permitted: Getter befriends Window
height.value = height; // same here
}
};
void someExternalFunction()
{
Window win;
win.resize(640,480); // Ok: public method
// This works: Getter::get() is public
std::cout << "Current window size: " << win.width.get() << 'x' << win.height.get() << ".\n";
// This doesn't work: Getter::value is private
win.width.value = 640;
win.height.value = 480;
}
그것은 공정하게,그리고 수도를 구현 get
논리를 사용하여 다른 부분 템플릿을 전문화는 속임수.동일 적용할 수 있는 일종의 세터 또는 GetterSetter 템플릿 클래스.
당신의 생각은 무엇입니까?
해결책
는 이 솔루션은 깔끔한에서 구현,관점 architectually,그만 중간에 있다.의 포인트 get/set 패턴을 제공하는 것입 clas 컨트롤을 통해 그것의 데이터 및 감소 커플링(i.e다른 클래스 알 는 방법 데이터가 저장된다).이 솔루션을 달성한 전지만 아주 후자입니다.
사실 다른 클래스에 이은 두 가지를 알아-의 변수 이름을과 방법에 게터(i.e .get()
어)에서 대신의 하나-예를 들어, getWidth()
.이로 인해 증가 연결할 수도 있습니다.
모든 말했다,이것은 분명한 건축 머리카락이다.그것이 문제가되지 않는 모두에서 많이 하루의 끝.
편집 확인,지금은 배설물과 웃음기 버전의 게터를 사용하여 연산자는,그래서 당신이 할 필요가 없 .value
나 .get()
template <class T>
struct TemplateParameterIndirection // This hack works for MinGW's GCC 4.4.1, dunno others
{
typedef T Type;
};
template <typename T,class Owner>
class Getter
{
public:
friend TemplateParameterIndirection<Owner>::Type; // Befriends template parameter
operator T()
{
return value;
}
protected:
T value;
T& operator=( T other )
{
value = other;
return value;
}
};
class Window
{
public:
Getter<int,Window> _width;
Getter<int,Window> _height;
void resize(int width,int height)
{
// do actual window resizing logic
_width = width; //using the operator
_height = height; //using the operator
}
};
void someExternalFunction()
{
Window win;
win.resize(640,480); // Ok: public method
int w2 = win._width; //using the operator
//win._height = 480; //KABOOM
}
편집 고정 하드 할당 연산자입니다.이 일을 해야 합리적으로 잘 유형의 경우에는 그 자체가 지정 연산자입니다.기본적으로 구조체가 사람들을 위한 간단한 사람이 그것을 밖으로 작동 합니다.
더 복잡한 클래스를 구현해야 할당 운영자는 공정한 충분합니다.가 RVO 고 사본에 쓰기 최적화 이해야 합리적으로 효율적입니다.
다른 팁
FWIW 여기에는 나의 의견이 당신의 질문:
- 일반적으로 이 지점이 있는 비즈니스 로직 또는 다른 제약 조건에서 적용하였습니다.할 수도 있습 계산 또는 가상의 변수를 디커플링 인스턴스의 변수에 접근 방법이 있습니다.
- 하지 않는 것으로 알고 있다.프로젝트가 일에 있었다의 가족 C 매크로 스탬프 이러한 방법
- Yes;내가 생각하는 매우 깔끔합니다.나는 그냥 그것을 고민하지 않는 문제 가치,그것은 혼동하는 다른 개발자(하나의 개념이 더 필요에 맞게 자신의 머리)지 절약 훨씬 넘는 각인 등의 방법을 수 있습니다.
이후 Igor Zevaka 게시는 하나의 버전이 나는 게시물 중 하나 나는 오랜 시간 전입니다.이것은 약간 다르-나에서 관찰하는 시간 가 의 실제 사용 get/set 쌍(는 실제로는 아무것도 않았다)었을 적용하는 변수 값을 내에서 머무는 사전에 결정된 범위입니다.이것은 좀 더 광범위한과 같은 추가 I/O 사업자가 추출기는 여전히 적용의 정의된 범위에 있습니다.그것은 또한 그것의 비트 테스트/코드를 표시의 일반적인 아이디어 그것이 무엇이며 어떻게 그것은 그것:
#include <exception>
#include <iostream>
#include <functional>
template <class T, class less=std::less<T> >
class bounded {
const T lower_, upper_;
T val_;
bool check(T const &value) {
return less()(value, lower_) || less()(upper_, value);
}
void assign(T const &value) {
if (check(value))
throw std::domain_error("Out of Range");
val_ = value;
}
public:
bounded(T const &lower, T const &upper)
: lower_(lower), upper_(upper) {}
bounded(bounded const &init)
: lower_(init.lower), upper_(init.upper)
{
assign(init);
}
bounded &operator=(T const &v) { assign(v); return *this; }
operator T() const { return val_; }
friend std::istream &operator>>(std::istream &is, bounded &b) {
T temp;
is >> temp;
if (b.check(temp))
is.setstate(std::ios::failbit);
else
b.val_ = temp;
return is;
}
};
#ifdef TEST
#include <iostream>
#include <sstream>
int main() {
bounded<int> x(0, 512);
try {
x = 21;
std::cout << x << std::endl;
x = 1024;
std::cout << x << std::endl;
}
catch(std::domain_error &e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
std::stringstream input("1 2048");
while (input>>x)
std::cout << x << std::endl;
return 0;
}
#endif
사용할 수도 있습니다 getter 또는 세터 입력되는 것을 방지 또는 집합 계산 가능한 값은,많은 방법이 속성에서 사용되는 다른 언어는 C#
나는 생각할 수 없는 합리적인 방법을 추상화하고 설정의의 값/속성입니다.
나는 익숙하지 않은 충분한 C++소 표준다.
이 과 이 경우에는지 확인해야 합니다 변호사/클라이언트의 관용구에 대한 현명한 우정을 사용한다.을 찾기 전에 이의 관용구,내가 피할 우정합니다.
그리고 지금 질문,그리고 무엇이 필요한 경우 setter
뿐만 아니라.
내가 당신에 대해 알고하지 않지만,나는 경향이 있(대략)두 가지 형태의 클래스:
- 클래스에 대한 로직
- blobs
양은 느슨한 컬렉션의 모든 속성의 비즈니스 개체입니다.예를 들어, Person
가 surname
, firstname
, 여러 주소,여러 가지 직업...그래서 Person
지 않을 수도 있는 논리입니다.
를 도울 수 있습니다.,내가 사용하는 경향이 정식 개인 특성에+getter+setter 때문에,그것은 초록의 실제 구현에서 클라이언트입니다.
그러나,하지만 이 템플릿(그리고 그것의 진화에 의해 이고르 Zeveka)은 정말 좋은,그들은하지 않는 주소를 설정에 문제가지 주소 바이너리 호환성 문제입니다.
난 것이 아마 리조트 매크로...
다음과 같습니다.
// Interface
// Not how DEFINE does not repeat the type ;)
#define DECLARE_VALUE(Object, Type, Name, Seq) **Black Magic Here**
#define DEFINE_VALUE(Object, Name, Seq) ** Black Magic Here**
// Obvious macros
#define DECLARE_VALUER_GETTER(Type, Name, Seq)\
public: boost::call_traits<Type>::const_reference Name() const
#define DEFINE_VALUE_GETTER(Object, Name)\
boost::call_traits<Name##_type>::const_reference Object::Name ()const\
{ return m_##Name; }
#define DECLARE_VALUE_SETTER(Object, Type, Name)\
public: Type& Name();\
public: Object& Name(boost::call_traits<Type>::param_type i);
#define DEFINE_VALUE_SETTER(Object, Name)\
Name##_type& Object::Name() { return m_##Name; }\
Object& Object::Name(boost::call_traits<Name##_type>::param_type i)\
{ m_##Name = i; return *this; }
는 것처럼 사용:
// window.h
DECLARE_VALUE(Window, int, width, (GETTER)(SETTER));
// window.cpp
DEFINE_VALUE(Window, width, (GETTER)); // setter needs a bit of logic
Window& Window::width(int i) // Always seems a waste not to return anything!
{
if (i < 0) throw std::logic_error();
m_width = i;
return *this;
} // Window::width
의 비트와 함께 전처리기 매직 그것은 확실히 잘 작동합니다!
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/rem.hpp>
#define DECLARE_VALUE_ITER(r, data, elem)\
DECLARE_VALUE_##elem ( BOOST_PP_TUPLE_REM(3)(data) )
#define DEFINE_VALUE_ITER(r, data, elem)\
DEFINE_VALUE_##elem ( BOOST_PP_TUPLE_REM(2)(data) )
#define DECLARE_VALUE(Object, Type, Name, Seq)\
public: typedef Type Name##_type;\
private: Type m_##Name;\
BOOST_PP_SEQ_FOREACH(DECLARE_VALUE_ITER, (Object, Type, Name), Seq)
#define DEFINE_VALUE(Object, Name, Seq)\
BOOST_PP_SEQ_FOREACH(DEFINE_VALUE_ITER, (Object, Name), Seq)
좋아,형식이 안전하지 않으며 모든지만,:
- 그것은 합리적인 설정의 매크로 생각
- 그것은 사용하기 쉽고,사용자만이 이제까지에 대해 걱정하 2 매크로 모든 후,그 다음과 같 템플 오류를 얻을 수 있는 털이
- 사용 합니다.call_traits 효율성에 대한(const&/값 선택)
거기에 더 많은 기능이 있다:get/set duo
그것은 불행하게도,정으로...고 불평하지 않습니다면 당신이 이제까지
- 이란 접근자(public,protected,private)도록하는 것은 좋지 않습니다 intersped 전체 클래스
여기에 정규를 들어 다음:
class Window
{
// Best get done with it
DECLARE_VALUE(Window, int, width, (GETTER));
DECLARE_VALUE(Window, int, height, (GETTER));
// don't know which is the current access level, so better define it
public:
};
당신이 해결이 잘못된 문제입니다.에서는 잘 설계된 응용 프로그램,현야 희귀,자동화되지 않습니다.는 의미있는 클래스가 제공하는 몇 가지 종류의 추출.그것은 단순히 컬렉션 멤버 모델하는 개념이상의 구성원 변수입니다.그리고 일반적으로도 의미를 노출하는 개별적인 회원입니다.
클래스에 노출시키는 작업을 의미 개념에서는 모델입니다.대부분의 회원은 변수가 있을지 이상 상태를 저장하는 당신이 필요합니다.하지만 그것은 일반적으로 수 없 직접 액세스. 그 이유는 그것은 개인의 구성원 등에서 첫 번째 장소.
보다는 쉽게 찾을 방법을 쓰기 car.getFrontLeftWheel()
, 자신에게 물어 이유로 클래스 사용자가 필요한 전면 왼쪽 휠에서 첫 번째 장소입니다.당신은 일반적으로 조작하는 휠을 직접 운전하는 경우?차를 돌의 모든 바퀴 회전하는 비즈니스에 대한 당신,그렇지 않나요?
이것은 생각 #define
s 은 여전히 유용합니다.
템플릿은 버전은 복잡하고 이해하기 어렵-정 버전은 분명
#define Getter(t, n)\
t n;\
t get_##n() { return n; }
class Window
{
Getter(int, height);
}
내가 있는 구문에 약간의 잘못을-하지만 당신은 점을 얻는다.
는 경우가 있었는 잘 알려진의 설정 템플릿에서,말씀,부스트 그때 나는 그들을 사용합니다.하지만 내가 쓸 것 나 자신입니다.