2013-03-07 3 views
0

두 클라이언트가 ZeroMQ PUB/SUB 소켓을 사용하여 통신 할 수있는 기능을 제공하는 라이브러리를 작성 중입니다. 연결 소켓의 몇 가지를 가지고 있으며, 각각의 포트를 통해 지정된 주소에 연결하도록 구성되어기본 클래스의 생성자에 대한 세부 사항을 추상화하기 위해 서브 클래스를 정의 할 때이 함수는 무엇이라고할까요?

class Connection { 
    Connection(const char* address, int outgoingPort, int incomingPort); 
}; 

: 각 클라이언트 응용 프로그램은 방송 엔드 포인트 또는 수신기 엔드 포인트, 그 엔드 포인트 클래스 중 하나가 연결 멤버를 인스턴스화합니다. 그러나 Connection이 실제로 인스턴스화 된 클래스에서 이러한 세부 정보를 노출하지는 않습니다. 기본 연결 개체에는 들어오는 포트와 나가는 포트가 있지만이 세부 사항은 나머지 프로그램을 스며들 필요가 없습니다. 상위 계층에서는 두 개의 지정된 포트 인 데이터 포트와 제어 포트로 생각하는 것이 더 현명합니다. 그래서 나는 어떤 포트가 들어오는 포트인지 그리고 특정 유형의 연결을위한 나가는 포트인지를 정의하는 생성자를 구현하는 두 개의 서브 클래스를 가지고있다.

class BroadcasterConnection : public Connection { 
    BroadcasterConnection(int dataPort, int controlPort) 
    :Connection("*", dataPort, controlPort) {} 
}; 
class ReceiverConnection : public Connection { 
    ReceiverConnection(const char* hostAddress, int dataPort, int controlPort) 
    :Connection(hostAddress, controlPort, dataPort) {} 
}; 

는 또한 브로드 안정된 종점으로 해당 포트에 결합하므로 실제 원격 주소 대신 "*"를 사용할 필요가있다. 다시 말하지만, 브로드 캐스터 연결을 인스턴스화하고 사용하는 클래스는이 세부 사항에 신경 쓰지 않아도되므로 BroadcasterConnection 생성자가 처리합니다.

또 다른 예로서 ZeroMQ 소켓을 감싸는 클래스와 동일한 작업을 수행합니다. 기본 소켓 클래스가 있고 하위 클래스 생성자는 ZeroMQ 헤더의 적절한 값 (ZMQ_PUB 또는 ZMQ_SUB)을 기본 소켓으로 전달합니다. 클라이언트가 ZeroMQ의 값을 직접 사용할 수 없으므로 공식적인 방식으로 PUB 소켓과 SUB 소켓의 구분을 체계화해야하며 단일 하위 클래스 생성자를 제공하는 것이 투명하고 합리적인 방법으로 보입니다.

class Socket: 
    Socket(void* context, const char* address, int port, int socketType); 

class PublishSocket : public Socket: 
    PublishSocket(void* context, const char* address, int port) 
    :Socket(context, address, port, ZMQ_PUB) {} 

class SubscribeSocket : public Socket: 
    SubscribeSocket(void* context, const char* address, int port) 
    :Socket(context, address, port, ZMQ_SUB) {} 

이 서브 클래스는 모든 공상 아무것도하지 않는,하지만 난 당신이 그들이 추상화의 서비스에 유용하고 건강한 또한있어 동의 바랍니다. 그러나 나는이 단순 관용구의 공통 이름을 알지 못한다. 서브 클래스를 정의 할 때만이 더 특수화 된 매개 변수 집합으로 객체를 생성 할 목적으로 생성자를 구현합니다. 무엇을하고 있습니까?

여기서 중요한 점은이 하위 클래스 이 추가 메서드 또는 데이터을 정의하지 않는다는 것입니다. 여기에 또 다른 예제가 있습니다. 여기에는 모든 유형의 엔터티를 식별하는 기본 Tag 클래스가 있습니다. 서브 클래스는 특정 도메인 매개 변수를 기반으로 개별 유형의 엔티티에 대한 태그를 작성하는 데 사용되지만 결국에는 결국 태그 오브젝트로 종결됩니다. 그래서

Tag(char typeIdentifier, int entityIdentifier); 

LightTag(int lightIndex):Tag('L', lightIndex) {} 
SkeletonTag(const char* skeletonName):Tag('S', hash(skeletonName)) {} 
CameraTag():Tag('C', 0) {} 

, 몇 가지 질문 :

  1. 이 관용구에 일반적으로 사용되는, Googleable 이름이 있습니까?

  2. Connection c = BroadcasterConnection(40001, 40002);을 쓰면 복사 생성자가 호출됩니다. BroadcasterConnection은 추가 데이터를 정의하지 않으므로 두 클래스는 서로 교환 가능해야하며 (RTTI에도 불구하고) 객체 조각화에 대한 걱정없이 다운 캐스트 할 수 있어야합니다. 맞습니까? 복사를 피하는 이런 식으로 객체를 생성하는 것과 비슷한 편리한 구문이 있습니까? 이것은 생성자 이니셜 라이저 목록에서도 발생하는 것으로 보입니다.

  3. 이것은 실제적인 예는 아니지만, Connection* c = new BroadcasterConnection(40001, 40002);으로 작성한 다음 delete c;으로 작성한다고 가정합니다. Connection은 가상 소멸자를 가지고 있지 않지만, 가상 함수가 없기 때문에 (그래서 vtable은 없다). BroadcasterConnection은 추가 데이터를 정의하지 않는 Connection의 직접 하위 클래스이므로이 작업이 안전할까요? BroadcasterConnection에서 일부 회원 데이터를 추가하면 어떻게됩니까? 그런 다음 메모리 누수가 발생합니까?

  4. 컴파일러가 추가 데이터를 포함 할 수 없도록 특정 하위 클래스가 위에서 설명한 방법으로 만 생성자라는 사실을 명시 적으로 규명하는 방법이 있습니까?

그리고 같은 문제를 근본적으로 해결하는 더 좋은 방법이 있다면, 나는 그것을 듣고 싶습니다.

답변

3

주의하십시오. 정의되지 않은 동작을 호출했습니다. 피연산자의 정적 유형이 동적 유형과 다른 경우 제 대안

5.3.5

3) (개체를 삭제), 정적 유형의 기본 클래스이어야한다 피연산자의 동적 유형 및 정적 유형은 가상 소멸자를 가져야하거나 동작이 정의되지 않았습니다. [...]

나는 거의 항상 작동 할 것이라고 생각하지만 정의되지 않은 동작을 호출하고 있으므로 컴파일러에게 하드 드라이브의 내용을 사진 프린터로 전자 메일로 보내고 크레딧을 사용하도록 요청했습니다. 카드를 지불해야합니다. 아니면 다른 느낌이 든다.

실제로, 아마도 기본 클래스 소멸자를 호출 할 것입니다.

위의 유틸리티 대부분은 기본 클래스 개체의 복사본을 파생 클래스 이름과 함께 반환하는 함수를 통해 처리 할 수 ​​있습니다. 즉, SubscribeSocket 클래스 대신 Socket을 반환하는 SubscribeSocket이라는 함수가 있습니다. 이동 의미 (Socket의 빠른 이동이 있습니까?) 및 RVO (SubscribeSocket의 구현을 기꺼이 전제로한다는 가정하에)이 방법이 효율적입니다.

귀하의 구성표에있는 한 가지 이점은 필요한 경우 Socket을 입력 할 수 있다는 것입니다. 정의되지 않은 동작을 호출하지 않고 작동하는 접근 방식은 Socket과 관련이없고 Socket을 소유하고 및 operator Socket const&() const을 갖는 클래스를 정의하여이를 허용합니다 (예 : Socket). Sockets이 필요한 API로 전달됩니다. 명시 적으로 필요할 때 .GetSocket() 메소드를 사용하십시오. operator=(Socket const&)을 피하고 슬라이스를 차단하십시오. 어쩌면 디버그에서 체크를하는 명시 적 "소켓에서 생성"유형의 함수를 가질 수 있습니다 ...