0

私のプログラムでは、Socket という名前のクラスで「ソケット インターフェイス」(実際には使用している部分のみ) をラップすることにしました。AF_PACKET と AF_INET という 2 つの異なる「ドメイン」を使用しています。これらのドメインはアドレス構造が異なるため、アドレスもラップすることにしました。

次のような小さなクラスを作成しました。

class SockAddress
{
    public:
    SockAddress();

    virtual sockaddr* getAddress();
    virtual socklen_t getAddrLen();

    private:

    sockaddr address_;
};

このクラスに基づいて、sockaddr*返される代わりに sockaddr_ll*` という別のクラスを作成しました。

virtual sockaddr_ll* getAddress();
...
sockaddr_ll address_;

C では、それらはすべてポインターです。sockaddr*aが必要な場合はどこでも a を渡すsockaddr_ll*(および対応するsocklen_tサイズの値を渡す) 場合、問題は発生しないことを単純に知っています。

しかし、C++ でコンパイルしようとすると、invalid covariant return typeエラーが発生します。このエラーについては、こちらこちらこちらを既に読んでおり、その意味を理解しています。とにかく、これを「回避」する方法が見つかりませんでした。

私の質問は: ポインターを返したいので (そしてポインターは同じサイズになります)、コンパイラーに を として受け入れるように強制する方法はありsockaddr_ll*ますsockaddr*か? また、方法があるとすれば、どのようにすればよいでしょうか。

(方法がない場合、この問題を解決するための「正しい方法」は何でしょうか?)

4

3 に答える 3

2

一般に、パブリック API がポインターを公開し、型キャストを奨励している場合は、設計をやり直す必要があります。

クラスで何かをラップするときのポイントは、構造体のようなホルダーを作成するだけでなく、詳細を隠すことです。

組み込みのように動作する新しい型を設計しています。これは、これらの厄介なポインターをすべてクラス内に隠すことを意味します。

たとえば、抽象的なものだけを参照するトップレベル クラスを作成してから、さまざまな低レベル API をサポートする場合はサブクラスにしますが、それらの API は、listen/accept/connect などの抽象的な操作の背後にあるサブクラスに完全にカプセル化します。

class Socket
{
   public:
      Socket(IPAddress address, int port);
      virtual ~Socket() = 0; // close/clean up resources
      virtual void connect() = 0;
      ...
};  

次に、Socket クラスをサブクラス化して、低レベル API の詳細をカプセル化します。

もう 1 つのオプションは、SockAddress が ops を隠し、処理対象の抽象ソケットを取得するように、物事を逆にすることです。

class SockAddress
{
      void doSomethingForSocket(Socket *s) { ... }
}

私は通常、最初に API を使用するプログラムを作成するように人々に言います。

Socket s("192.168.5.100", 80);
s.connect();
int i;
i << s;
...
ServerSocket server("*", 80);
Socket *client;
while((client = server.accept()) != null) {
   ...
}

次に、その API の作成に取り掛かります。API 自体をビルドするときも同じ手法を使用します。connect() を書くとき、存在するかもしれない「素敵なもの」の次の下位セットは何ですか。

于 2012-09-05T18:26:44.273 に答える
0

Cでは、これらはすべてポインターです。sockaddr_ll *を渡すと、どこでもsockaddr*が必要になることがわかります。

その断言の参考にしてください。Cのエイリアシングルールに違反していると確信しています。

使用していたコンパイラで使用できなかったときに、このような共変リターンを実装しました。

class SockAddress
{
public:
    SockAddress();

    sockaddr* getAddress() { return doGetAddress(); }

protected:
    virtual sockaddr* doGetAddress() { return ...; } 
};

class SockAddress_ll: public SockAddress
{
public:
    SockAddress();
    sockaddr_ll* getAddress() { return static_cast<sockaddr_ll*>(doGetAddress()); }

protected:
    sockaddr* doGetAddress() { return ...; }
};

reinterpret_castによってstatic_castを変更すると、おそらくあなたが望むことをするでしょう。それを行うのがどれほど賢明かは別の問題です。

于 2012-09-05T18:23:39.140 に答える
0

まず第一に、仮想関数では、本当に同じ戻り値の型が必要です。仮想関数が使用されるため、オブジェクトの種類に基づいて正しいバージョンが使用され、同じ関数の異なるバージョンを使用できるため、これは当然のことです。したがって、関数を呼び出す人は、関数のどのバージョンが使用されるかを知らないため、戻り値の型が異なる場合はわかりません。

したがって、いずれの場合も sockaddr を返します。呼び出し元が返される実際の型を知る必要があることがわかった場合、最初から仮想関数を使用したくないか、非仮想の「getSpecificAddress」などを使用したいでしょう。

于 2012-09-05T18:24:26.223 に答える