2015-02-01 3 views
2

몇 가지 유형의 컨테이너에서 그룹화하고 일부 유형의 식별자로 액세스하려는 많은 객체가있는 클래스가 있습니다. 나는 그들에게, menu.title처럼 액세스 할 수 있지만 그때는 컨테이너, vector.push_back(title)에 추가 할 때마다 이름을 다시 입력해야하기 때문에클래스의 객체를 포함하는 최상의 C++ 디자인은 무엇입니까?

class Menu { 
Object title; 
Object play; 
Object instructions; 
Object pause; 
... 
}; 

위의 클래스에 나열된 각 객체를 갖는, 좋은 것입니다.

다음은 내가이 문제를 어떻게 해결했는지 보여줍니다. 열거 형의 정수 값을 사용하여 해당 색인에 액세스합니다. 이 간접 것 때문에 objects[TITLE] 또는 objects[PLAY]

class Menu { 
std::vector<Object> objects; 
}; 

enum ObjectTypes { 
TITLE, PLAY, INSTRUCTIONS, PAUSE, ... 
}; 

나는 일반적으로이 방법을 싫어하고, 중첩 된 클래스와 열거 형을 사용하는 경우, 식별자는 길고 복잡 될 수 있습니다. 나는 C에서 왔고 C++에서는 다소 새로운 것 같다. 누구든지이 문제를 해결하기 위해보다 실용적이고 우아한 방법이 있습니까? C++ 11 이상 환영!

+1

'std :: map'을 고려 했습니까? ''title ''이'std :: string' 인 곳에서'menu [ "title"]'을 사용하여 객체를 참조 할 수 있다는 것을 의미하는 연관 컨테이너입니다. – yizzlez

+0

잘 알려진 위치의 참조 인 메소드를 만들거나 그것은 배열입니다. 생성시 바인딩 할 때 간단합니다. –

+1

std :: map 일 수도 있습니다. – BitTickler

답변

1

사용중인 접근 방식이 좋습니다. 성가신 식별자를 피하려면 일시적인 참조를 만들어보다 간결하게 유지하십시오. 대신 호출의 예를 들면 다음과 같습니다

menu.objects[PAUSE].foo(); 
menu.objects[PAUSE].bar(); 
menu.objects[PAUSE].baz(); 

...이 필요한 경우 할 수있는 :

Object & pause = menu.objects[PAUSE]; 
pause.foo(); 
pause.bar(); 
pause.baz(); 

을하고 그것을 작동하는 것과 같은,하지만 중복 모든 문자없이.

0

나는 귀하의 접근 방식이 좋다고 생각합니다. std::vector 대신 std::map<ObjectType, Object>을 사용하면 좀 더 유형 안전성이 있습니다.

Object& operator[](index_type i){ return objects[i]; } 
const Object& operator[](index_type i) const { return objects[i]; } 

Then you can writemenu[TITLE] : 당신이 자신에게 약간의 입력을 저장하려는 경우

어느 쪽이든, 당신은 operator[]Menu에 과부하 수 있습니다.

그 외에도 덜 복잡 할 수있는 방법은 없지만 반복되는 정보는 없으며 Jeremy가 지적한 것처럼 개체를 여러 번 사용해야하는 경우 항상 로컬 참조 auto& title = menu[TITLE];을 만들 수 있습니다.

는 다른 어떤 책임 Menu에 따라 어쩌면 당신은 모두에 Menu 클래스를 필요로하지 않는 한 당신은 단지 map 또는 vector 직접 사용할 수 있습니까?

0

질문에 대한 최상의 해결책은 주로 사용 사례에 달려 있습니다. 나는 두 가지 주요 사용 사례를 참조하십시오

당신은 당신의 코드에서 해당 "장치"를 조작 할 때 당신이 읽을 수있는 코드를 달성 할 수 있도록 "장치"의 "기능"을 대표 할
  1. . 재생, 중지, 일시 중지 작업이있는 MediaPlayer 기기 등. 그러나 튜너 장치와 같은 다른 장치에도 재생 코드를 다시 사용하려는 경우 Play()와 같은 멤버 개체를 "장치"개체에 단순히 추가하는 옵션을 해제했습니다. 또한 Enable()/Disable()/ToString()/Trigger(), Configure() 등 모든 "또는"하위 집합에 작업을 적용하려는 경우 멤버 함수가 접근 방식은 바람직하지 않다.
  2. 더 많은 데이터에 초점을 맞춘 문서 개체 모델을 만들고 싶습니다. Xml 문서와 같은.

질문에서 쓴 내용부터 유스 케이스 1을 염두에 두어야합니다. Object 유형에는 필요한 모든 일반적인 조작이 있습니다.
그러나 이러한 모든 "기능"에는 차이점이 있습니다.

당신의 간단한 방법에 충실하기 위해, 당신은 장기적으로 짜증나는하지만, 거의 피할 것으로 판명 수있는 수동으로 개체 인스턴스를 구성/설정해야합니다 : 또한

// Example of "annoying": If you have more than 1 "device", 
// you have to write such a function for each of them. 
// Also, there is that fishy bit with Object.Id - the association between 
// your configuration data and your object you want to configure. 
void ConfigureMenu(std::vector& menuItems, MenuConfigurationData& configData) 
{ 
    for(auto& menuItem : menuItems) 
    { 
     menuItem.Configure(configData[menuItem.Id]); // spoiler! 
    } 
} 

, 나는 지금도 당신의 질문에 표시되지 않은 코드를 가지고 있다고 생각하는데, 이것은 당신의 객체를 구성한다.

이 점을 염두에두고 "장치"당 1 개의 클래스 유형을 손으로 직접 작성하는 것이 좋습니다. 다음 장치/메뉴는 동일한 코딩으로 처리해야하며 더 많은 코딩이 필요합니다.

그럼, 내 조언은 당신의 문제를 추상화하고 문제를 모델링하는 것 대신에 class Menu을 제거하는 것입니다. 객체는 "함수"이고 디바이스는 단순히 함수/객체 집합입니다. 그러면 class Menu이 단순히 인스턴스라는 메뉴가됩니다.

typedef uint32_t FunctionId; // for example uint32_t... 
typedef std::map<FunctionId,Object> Device; // aka. Menu. 

그런 다음, 당신이 가장 가능성이 어쨌든이 구성 기능에 해당 Device 맵의 인스턴스에 전달하고 구성 기능이 제대로 구성 개체로 채 웁니다.

// those enums are still specific to your concrete device (here Menu) but 
// you can consider the time it takes writing them an investment which will 
// pay off later when you write your code, using your functions. 
// You assign the function id which is used in your meta-data. 
enum class MenuFunctions : FunctionId { play = ..., title = ..., instructions, ... }; 

// "generic" configuration function. 
// Does not only configure your Object, but also puts it inside the device. 
void ConfigureDevice(Device& device, ConfigData& configData) 
{ // ... 
} 

그리고 나중에 코드에서, 당신은 다음과 같은 기능에 액세스 할 수 있습니다

menu[MenuFunctions::play].Trigger(); 

물론,이 대안 적 방법 및 변형이 있습니다. 예를 들어 메타 데이터 (구성 데이터, 장치 설명)가 있다고 가정하면 모든 코드를 직접 코딩하지 않고 대신 작업을 수행하는 코드 생성기를 작성할 수 있습니다.
첫 번째 버전의 생성기로 구성 기능과 열거 형을 만들 수 있습니다.

"중첩 된 클래스"의 사용 사례는 바로 Device 인스턴스의 컬렉션을 만드는 문제가됩니다. 이제는 모든 장치가 동일한 유형이므로, 여유있게 구성하고 하위 그룹으로 구성 할 수 있습니다.

0

아래의 몇 가지 다른 접근 방식. 나는 "최고"라는 말은하지 않습니다. 그들 모두에게 장단점이 있습니다.

가) 벡터를 사용하는 대신 요소 수를 일정하게 유지하려면 class Menu {std::array<Object,NObjectTypes> objects;}; 어레이를 사용하십시오.

B)는 그냥 객체에 대한 참조의 std::array<>을 반환하는 API를 클래스를 사용하지만, 제공 : 당신의 유형이 모두 동일하지 않을 경우

class Menu { 
Object title; 
Object play; 
Object instructions; 
Object pause; 
... 
std::array<Object*,NObjects> allObjects(); 
}; 

C) std::tuple 유용 할 수 있습니다.


메뉴의 경우 "A"로 자주 이동합니다.

관련 문제