[[C++による単純なserver program]]

前節の[[C++による単純なserver program]]は''典型的な誤り例''であると述べた。
ここではこの問題を考察しよう。

どこが誤りであるかというと、mysocket class で
copy constructor と代入演算子が定義
されていない、すなわち default のそれらが使われている点である。
default では各メンバーデータはそのまま代入される。つまり
socket descriptor は int なので、int の代入が起こる。

しかし socket descriptor は socket object を int で表現している
だけであって数学上の int ではない。四則演算ができるわけではない。
代入もしかり。単純に同じ値にすればよいというものではない。

前節の例では accept() でこの問題が起こっている。accept() は
受け入れた新しい mysocket object を返す。
accept関数の中では、mysocket object である tmpsock を
用意し、このデータメンバーを設定して tmpsock を戻り値にして
いる。すなわち tmpsock を呼び出し側 stack へコピーして
関数は終了し、そこから呼び出し側に用意された空の mysocket t へ
代入される。

では tmpsock はどうなるだろう?関数から抜ける時点で scope から
抜けるので destructor が呼ばれる。destructor は socket descriptor を
close する。今仮に accept した socket descriptor が 5 だとすると、
5 が closeされる。にもかかわらず 5 は呼び出し側の t の socket descriptor の
値でもある。つまり t という object は以後 close された 5 に対し
操作を行うことになる。これが動作しない理由である。

では、この問題の解決はどうすればよいか?
システムにはこのような目的のために descriptor を複写する関数が
用意されている。これを使わなければならない。
具体的には dup関数(Windows の場合は DuplicateHandle関数)を
使う。

dup関数は、同一実体に対し、異なる descriptor 値を割り当てて
くれる。dup関数を使えばシステムは同一実体に対し、
複数の descriptor を与えたことを認識してくれるので、
一方を close しても実体そのものは解放しない。
割り当てたすべての descriptor が close された時点で初めて実体そのものも
解放する。従って、例えば descriptor 5 に対し、dup により 7 が与えられた
とすると、5 を close しても 7 は使える。

というわけで、copy constructor と代入演算子を宣言する。
 #ifndef MYSOCKET_H_INCLUDED
 #define MYSOCKET_H_INCLUDED
 
 #include <unistd.h>
 #include <netinet/in.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 
 class mysocket
 {
 public:
   mysocket();
   mysocket(const mysocket& from);
   mysocket(int domain, int type, int protocol=0);
   virtual ~mysocket();
   virtual mysocket& operator=(const mysocket& from);
   virtual int close();
   virtual int connect(const struct sockaddr* addr, socklen_t len);
   virtual int bind(const struct sockaddr* addr, socklen_t len);
   virtual int listen(int backlog);
   virtual mysocket accept(struct sockaddr* addr, socklen_t* addrlen);
   virtual int send(const void* msg, size_t len, int flags=0);
   virtual int recv(void* msg, size_t len, int flags=0);
 
 protected:
   int m_sd;
 };
 
 #endif

つけ加わった copy constructor と代入演算子は dup を使って

 mysocket::mysocket(const mysocket& from)
 {
   m_sd = -1;
   if(from.m_sd != -1)
     m_sd = ::dup(from.m_sd);
 }
 
 mysocket& mysocket::operator=(const mysocket& from)
 {
   if(&from == this)
     return *this;
   close();
   if(from.m_sd != -1)
     m_sd = ::dup(from.m_sd);
   return *this;
 }

と書ける。

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS