Access Violation について

[戻る]
RAPT [E-Mail] 2003/02/20(木) 02:45:48
開発環境:Windows98/VC++6.0sp5pro/SDK-may2002/ATL3.0/WTL7.0
※MFC不使用

後掲のソースで、Func()を抜けるときに以下のエラーが発生します。
なお、Func()は、別の関数から呼ばれています。

> ハンドルされていない例外は MyApp.exeにあります:
> 0xC0000005: Access Violation。

//------------------------------------------------------------
// ...\Vc98\Include\List.h
//------------------------------------------------------------
template<class _Ty, class _A = allocator<_Ty> >
class list {
    class iterator : public const_iterator {
        iterator& operator++()
            {_Ptr = _Acc::_Next(_Ptr);  // ← ここでブレーク
            return (*this); }


//------------------------------------------------------------
// 以下、自作ソースの一部
//------------------------------------------------------------
template <class T>
class MyList
{
protected:
    typedef std::list<T> _MyList;
    _MyList* m_List;
    _MyList::iterator m_it;
    int m_nSeekPtr;

public:
    MyList() : m_List(NULL) {
        m_List = new _MyList;
    }
    virtual ~MyList() {
        delete m_List;
    }
    int GetSize() const {
        return static_cast<int>(m_List->size()); // m_List->size() 
returns size_t
    }
    T GetAt(const int& nIndex = 0) {
        m_it = m_List->begin();
        std::advance(m_it, nIndex);
        return *m_it;
    }
    void Append(const T& objNewData) {
        m_List->push_back(objNewData);
    }
};
class MyCStringList : public MyList<CString> {}; // 省略なし

BOOL MakeSection(const MyCStringList& listrGot, LPCTSTR szFile, LPCTSTR 
szSection, const DWORD& dwBufferSize = SHRT_MAX - 1) {
    MyCStringList listrData = static_cast<MyCStringList>(listrGot);

    TCHAR *pszWriteData = new TCHAR [dwBufferSize + 1];
    ::memset(pszWriteData, 0, static_cast<size_t>(dwBufferSize + 1));

    int nPtr = 0, nLen = 0;
    CString str;
    for (DWORD i = 0; i < listrData.GetSize(); ++i) {
        str.Format(_T("%02ld="), i + 1);
        str += listrData.GetAt(i);
        nLen = str.GetLength() + 1;
        if (nPtr + nLen > dwBufferSize) { // バッファー不足
            delete [] pszWriteData;
            return FALSE;
        }
        ::lstrcpyn(pszWriteData + nPtr, str + _T("\0\0"), nLen + 1);
        nPtr += nLen;
    }

    // output data
    const int nRet = ::WritePrivateProfileSection(szSection, 
pszWriteData, szFile);
    ::WritePrivateProfileString(NULL, NULL, NULL, szFile); // バッファをフ
ラッシュ
    delete [] pszWriteData;
    return (nRet) ? TRUE : FALSE;
}

void Func() {
    MyCStringList listrTest;

    // テスト用文字列を追加
    listrTest.Append(_T("string1"));
    listrTest.Append(_T("string2"));
    listrTest.Append(_T("string3"));

    CString strPath = _T("");
    ::GetModuleFileName(_Module.GetModuleInstance(), 
strPath.GetBufferSetLength(MAX_PATH), MAX_PATH);
    strPath.ReleaseBuffer();
    const int nPos = strPath.ReverseFind(_T('\\'));
    if (nPos < 0) {
        ATLASSERT( FALSE );
        return;
    }

    strPath = strPath.Left(nPos + 1) + _T("test.ini");

    BOOL bRet = MakeSection(listrTest, static_cast<LPCTSTR>(strPath), _T
("test"), 100);
    ATLASSERT( bRet );
}


//------------------------------------------------------------
// Test.ini への出力結果
//------------------------------------------------------------
[test]
01=string1
02=string2
03=string3


(1) デバッガで調べたところ、MakeSection() にて、
MyCStringList listrData = static_cast<MyCStringList>(listrGot);
としたときの listrData と listrGotの中身のアドレスが同じなのです。
私は、こうすると別のオブジェクトが用意され、中身が物理的に複製
されると考えていたのですが、間違っているのでしょうか。

(2) また、listrData と、listrGot が指す先が同一だとすると、
MakeSection()を抜けるときに、listrGot が開放され、その結果
listrData の値が不定となり、アクセス違反が発生してまう、
ということなのでしょうか。
 →さらに、この場合、どのように対処すればよろしいのでしょうか。

(3) その他、何かおかしいところがあれば、指摘くださると幸いです。

επιστημη 2003/02/20(木) 08:58:00
MyList<T>::m_List は list<T>の'ポインタ'です。

   MyCStringList listData = static_cast<MyCStringList>(listrGot);

によってコピーが作られますが、ポインタのコピーは一つの実体を
複数のポインタが指している状態となります。
よーするに listData.m_List == listGot.m_List です。
listDataがデストラクトされるとき、delete m_List しているので、
元ネタをもぶっ壊します。

RAPT [E-Mail] 2003/02/20(木) 23:14:30
επιστημηさん、回答ありがとうございます。

CStringクラスを参考に、参照カウンタを持たせようかとも思いましたが、
私には実装は難しすぎたため、ポインタで保持するのを止め、
> _MyList* m_List;
_MyList m_List; とすることにしました。

ありがとうございました。

επιστημη 2003/02/21(金) 00:29:05
boost::shared_ptrあたりを使ってみますか?

RAPT [E-Mail] 2003/02/21(金) 11:39:06
boost::shared_ptr ですか。いいですね。
今度試してみます。貴重な情報ありがとうございます。

古いスレッドには追加発言できません。