[[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;
}
と書ける。