自作のスマートポインタについて、NULLを渡す

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ponkikki
記事: 7
登録日時: 10年前

自作のスマートポインタについて、NULLを渡す

#1

投稿記事 by ponkikki » 10年前

お世話になっております。
C++を勉強中なのですが、スマートポインタについて質問があります。

自作クラス(Node)のポインタをvector配列に入れてるのですが、メモリ管理が大変になるので、自作クラスをスマートポインタで管理しようとしました。
以下に実装したスマートポインタを示します。

コード:


#ifndef TestSmart_ptr_smart_ptr_h
#define TestSmart_ptr_smart_ptr_h

#define SAFE_DELETE(p) if(p){delete p;p=0;}

template <typename T>
class smart_ptr{
    
public:
    explicit smart_ptr(T* src = NULL); //コンストラクタ
    smart_ptr(const smart_ptr<T> &src);//コピーコンストラクタ
   
    smart_ptr<T>& operator =(const smart_ptr<T> &src); 
    virtual ~smart_ptr();//デストラクタ
    
    T& operator*(){ return *m_pPtr; }//*関数
    T* operator->(){ return m_pPtr; } //->関数
    
    void AddRef(){ (*m_pRefCnt)++; }
    void Release();
    
private:
    T* m_pPtr; //T型へのポインタ
    int *m_pRefCnt; //参照カウンタ変数
};


//--------------------------------------------------------------------------------------------
//@@@@@@@@@@@@@@@@@@@コンストラクタ@@@@@@@@@@@@@@@@@@@@@
//--------------------------------------------------------------------------------------------

template<typename T> smart_ptr<T>::smart_ptr(T* src)
{
    //Tへのポインタを保持
    m_pPtr = src;
    
    //ポインタの先に実体が存在する場合
    if (m_pPtr) {
        //参照カウンタを作成
        m_pRefCnt = new int;  //参照カウンタへのポインタを作成
        *m_pRefCnt = 0; //参照カウンタへのポインタを0にします
        
        AddRef(); //参照カウンタを1にします
    }
}

//コピーコンストラクタ
template <typename T> smart_ptr<T>::smart_ptr(const smart_ptr<T> &src)
{
    printf("コピーコンストラクタが呼ばれました\n");
    
    //ポインタコピー
    m_pRefCnt = src.m_pRefCnt;
    m_pPtr = src.m_pPtr;
    
    //コピー先はコピー元からも参照されているので、参照カウンタを1増加させる
    AddRef();
}


//--------------------------------------------------------------------------------------------
//@@@@@@@@@@@@@@@@@@@デストラクタ@@@@@@@@@@@@@@@@@@@@@

//--------------------------------------------------------------------------------------------


template <typename T> smart_ptr<T>::~smart_ptr<T>()
{
    Release();
}




//--------------------------------------------------------------------------------------------
//@@@@@@@@@@@@@@@@@@@演算子のオーバーロード@@@@@@@@@@@@@@@@@@@@@
//--------------------------------------------------------------------------------------------

template <typename T> smart_ptr<T>& smart_ptr<T>::operator=(const smart_ptr<T> &src)
{
      if (*src.m_pRefCnt == 0) {
        Release();
      }else{
        
        //自分自身へのコピーは意味がないので行わない
        //例
        //smart_ptr<Ship> pShip(new Ship(10));
        //pShip = pShip; <-意味がない
        if (m_pPtr == src.m_pPtr) return (*this);
        
        
        //自分の参照カウンタを1つ減少
        Release();
        
        //ポインタコピー
        m_pRefCnt = src.m_pRefCnt;
        m_pPtr = src.m_pPtr;
        
        
        AddRef();
    }
    
}

template <typename T> void smart_ptr<T>::Release()
{
    if (m_pRefCnt) {
        
        (*m_pRefCnt)--;
        
        if ( (*m_pRefCnt) >0 ) return;
        
        //もし参照カウンタが0だったら、ポインタ先の実体と参照カウンタへのポインタを削除
        SAFE_DELETE(m_pPtr);
        SAFE_DELETE(m_pRefCnt);
        
    }
}

#endif




shared_ptrを使いたかったのですが、使用しているゲームエンジンがc++11をサポートしてないので、自作でスマートポインタ(smart_ptr)を実装しました。
smart_ptrを用いてNodeクラスを管理してる様子を以下に簡単なサンプルコードで示してます。

コード:


#include <iostream>
#include <vector>
#include "smart_ptr.h"



class Ship{
    
public:
    Ship(int var)
    :x(var)
    {}
 
    ~Ship(){}
    
    int getVar(){ return x; }
    
    bool operator == (Ship ship){ return this->getVar() == ship.getVar(); }
  
private:
    
    int x;
    
};



//変数
typedef smart_ptr<Ship> shipPtr;
typedef std::vector<smart_ptr<Ship>> ships;


//関数オブジェクト
class Find{
    
public:
     shipPtr operator()(ships &list ,shipPtr pNode);
};

shipPtr Find::operator()(ships &list, shipPtr pNode)
{
    int i=0;
    for (ships::iterator it = list.begin(); it != list.end(); ++it) {
        i++;
        shipPtr pNodeItem = (*it);
        if (*pNodeItem == *pNode) {
            printf("%d 回目で見つかりました\n",i);
            return pNodeItem;
        }
    }
    
    return NULL; //①ここについてです。
}



int main(int argc, const char * argv[])
{
    
    ships list;
    
    shipPtr pShip(new Ship(10));
    list.push_back(pShip);
  
    shipPtr pShip2 = pShip;
    list.push_back(pShip2);
    
    shipPtr pShip3(new Ship(30));
    list.push_back(pShip3);
    
    shipPtr pShip10(NULL);

    Find find;
    
    //関数オブジェクトを呼んでいます
    pShip10 = find(list,pShip2);
    
    
    return 0;
}

コード中のcompare関数の①の部分でNULLを返したいのですが、
No viable conversion from long to shipPtr
とコンパイラに言われてしまいました。
NULLを返して、pShip10で受け取るので、スマートポインタ中の代入演算子のオーバーロードのところをいじれば良いのかなと思ったのですが、どのように変更を施せばよいのでしょうか?

ソーン

Re: 自作のスマートポインタについて、NULLを渡す

#2

投稿記事 by ソーン » 10年前

return NULL; という表記にはこだわりますか?
return shipPtr(NULL); や、return reinterpret_cast<Ship*>(NULL); 、さらにはreturn shipPtr(reinterpret_cast<Ship*>(NULL)); ではどうでしょう。
nullptrがサポートされていれば楽なのですが。

アバター
h2so5
副管理人
記事: 2212
登録日時: 13年前
住所: 東京
連絡を取る:

Re: 自作のスマートポインタについて、NULLを渡す

#3

投稿記事 by h2so5 » 10年前

C++03ではnullptr_tが使えないのでNULLを暗黙的にスマートポインタに変換することはできないと思います。

ponkikki
記事: 7
登録日時: 10年前

Re: 自作のスマートポインタについて、NULLを渡す

#4

投稿記事 by ponkikki » 10年前

ご回答ありがとうございます。
shipPtr(NULL)でエラーは出なくなりましたが、他でエラー出てしまったので、もう一度見直してみたいと思います。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: 自作のスマートポインタについて、NULLを渡す

#5

投稿記事 by YuO » 10年前

そもそも,T*からsmart_ptr<T>への自動変換させないようにするために,template <typename T> smart_ptr<T>::smart_ptr (T *)にexplicitをつけたのですよね。
元の質問はsmart_ptrとしては意図したとおりの動作だった,ということになります。

で,smart_ptrに空ポインタ渡すと,m_pRefCntが設定されないので不定値になりますよ。
せめてNULLを渡して,m_pRefCnt使用時に確認するようにしないと危険です。

あと,SAFE_DELETE(p)のif文は不要です。delete NULL;は何もしないことになっていますので。
複文マクロなので,do-whileハックを使うべき気がしますが (実際にSAFE_DELETEを使ったときに;をつけていますし),そもそもマクロよりinline関数を使うべきでしょうから,

コード:

template <typename U> inline void safe_delete (U*& ptr)
{
    delete ptr;
    ptr = 0;
}
のようなコードを使った方がよいでしょう (動作未確認)。
オフトピック
C++でマクロを使うのは,inline関数やconst定数で取り扱えない場合のみに限った方がよいです。
名前空間を無視する,型がない,等の理由により,プログラム構造を壊しかねません。

また,do-whileハックというのは,複文をマクロで定義するときに全体をdo {と}while(0)で囲む,というものです。
if文に直接ぶらさげた時に,呼び出しの末尾に;をつけるとelseがエラーになる,というのを防ぎます。

閉鎖

“C言語何でも質問掲示板” へ戻る