質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.37%

自作STLクラスのイテレータが正しく動作しません

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,576

score 15

前提・実現したいこと

課題で提示されたコードを元に、vectorとlist用の簡易イテレータを自作しているのですが、コンパイルしようとすると大量のエラーメッセージがでてしまいます。よく調べてみると、どうやらiteratorクラスがvector, listのメンバーではない故にエラーが起こっているようですが、ちゃんと親クラス?内にiteratorクラスを作っているのでなんでこうなるのかわかりません…どう解決すればいいのでしょうか…

このエラーばかり出てコンパイルできないので、肝心のiterator内の関数の挙動が確認できず、正しくかけているのかわかりません。もしよろしければ、iteratorクラスが正しく書けているかチェックして、間違えている所などあれば指摘や助言などしてくださるとありがたいです。

main.cppを改変することは許可されていないので、
mainをいじらない解決方法を教えてくださると助かります。

発生している問題・エラーメッセージ

E0135 class "Vector<int>" has no member "Iterator"などなど百件以上…

該当のソースコード

template<typename T>
class Vector
{
    T* mData;
    int mSize;
    int mCapacity;

    static T sUndefined;
public:


    Vector()// O(1)
    {
        mSize = 0;
        mCapacity = 15;
        mData = new T[mCapacity];
    }
    Vector(const Vector<T>& tOther) : Vector(); // O(n)

    Vector& operator =(const Vector<T>& tRHS); // O(n)

    ~Vector();

    void PushBack(const T& tItem); // O(1)

    void PopBack();// O(1)

    void PushFront(const T& tItem);// O(n)

    void PopFront(); // O(n)

    T& At(int tWhere) const; // O(1)

    void Clear(); // O(1)

    int Size() const; // O(1)

    void Reserve(int tCount); // O(n)

    int Capacity() const; // O(1)


    class Iterator
    {
        int mCurrentIndex = 0;
        Vector<T> *mMyVector = nullptr;       
        friend class Vector<T>;

        Iterator(Vector<T> *tWho, int tLocation)
        {
            mMyVector = tWho;
            mCurrentIndex = tLocation;
        }
    public:
        T& GetData()
        {
            if (mMyVector[mCurrentIndex] != nullptr)
            {
                return mMyVector[mCurrentIndex];
            }
            return sUndefined;
        }
        void Next()
        {
            mCurrentIndex += 1;
        }
        bool IsEqual(const Iterator& rhs)
        {
            int tCount = 0;
            mCurrentIndex = 0;
            int tSize = mMyVector->Size();

            while (tSize != mCurrentIndex-1)
            {
                if (mMyVector[mCurrentIndex] == rhs.GetData)
                {
                    tCount++;
                }
                mCurrentIndex++;
            }

            if (tCount == tSize) 
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    };

    Iterator Insert(Iterator tWhere, const T& tWhat) 
    {
        if (mSize == mCapacity)
            Reserve(2 * mCapacity);

        int tSize = 0;
        int tRet = 0;
        T* tData = new T[mCapacity];

        for (int i = 0; i < mSize; i++)
        {
            if(mData[i] == tWhere)
            {
                tData[tSize] = tWhat;
                tSize++;
                tRet = i + 1;        
            }
            tData[tSize] = mData[i];
            ++tSize;
        }

        delete[] mData;
        mData = tData;
        mSize = tSize;

        Iterator tAnswer(mData, tRet);
        return tAnswer;
    }
    Iterator Erase(Iterator tWhere)
    {
        int tSize = 0;
        int tRet = 0;
        T* tData = new T[mCapacity];

        for (int i = 0; i < mSize; i++)
        {
            if (mData[i] == tWhere)
            {
                ++tSize;
                tData[i] = mData[tSize];
                tRet = i+1;    
            }
            tData[i] = mData[tSize];
            ++tSize;
        }

        delete[] mData;
        mData = tData;
        mSize = i;

        Iterator tAnswer(mData, tRet);
        return tAnswer;
    }
    Iterator Begin()
    {
        Iterator tAnswer(mData, 0);
        return tAnswer;
    }
    Iterator End()
    {
        Iterator tAnswer(mData, mSize);
        return tAnswer;
    }
};

template<typename T>
T Vector<T>::sUndefined;
template <typename T>
class List
{
    struct ListNode
    {
        ListNode()
        {
            mPrev = nullptr;
            mNext = nullptr;
        }
        T mData;
        ListNode* mPrev;
        ListNode* mNext;
    };

    ListNode* mHead;
    ListNode* mTail;

public:
    List()
    {
        //mHead = nullptr;// Technically works, but we gain so much with sentinel nodes
        //mTail = nullptr;

        mHead = new ListNode;// You always know there is a node to the left and right
        mTail = new ListNode;
        mHead->mNext = mTail;
        mTail->mPrev = mHead;
    }
    List(const List& tOther) : List();

    List& operator = (const List& tRHS);

    ~List();

    void PushFront(const T& tWhat);

    void PopFront();

    static T sUnspecifiedStaticVar;

    T& Front();

    void PushBack(const T& tWhat);

    void PopBack();

    T& Back();

    int Size();

    void Clear();

    T& At(int tWhere) const;

    class Iterator
    {
        ListNode* mCurrent;
        friend class List<T>;
        Iterator(ListNode* tStart)
        {
            mCurrent = tStart;
        }
    public:
        T& GetData()
        {
            return mCurrent->mData;
        }
        void Next()
        {
            mCurrent = mCurrent->mNext;
        }
        bool IsEqual(const Iterator& rhs) const
        {
            return mCurrent == rhs.mCurrent;
        }
    };

    Iterator Insert(Iterator tWhere, const T& tWhat) 
    {
        ListNode* tNew = new ListNode;
        tNew->mData = tWhat;

        ListNode* tPrev = tWhere;
        ListNode* tNext = tPrev->mNext;
        tNext->mPrev = tPrev;

        tPrev->mNext = tNew;
        tNew->mPrev = tPrev;
        tNew->mNext = tNext;
        tNext->mPrev = tNew;

        return Iterator(tNew->mNext);
    }
    Iterator Erase(Iterator tWhat)
    {
        ListNode* tDead = tWhat;
        ListNode* tNext = tDead->mNext;
        ListNode* tPrev = tDead->mPrev;
        tPre->mNext = tDead;
        tNext->mPrev = tDead;
        delete tDead;

        tPrev->mNext = tNext;
        tNext->mPrev = tPrev;

        return Iterator(tNext);

    }
    Iterator Begin()
    {
        return Iterator(mHead->mNext);
    }
    Iterator End()
    {
        return Iterator(mTail);
    }
};

VectorとListの関数を簡略しているので、このままコンパイルはできません。
こんな感じのmainで動かせればいいなと思います。

#include <iostream>
#include "List.h"
#include "Vector.h"

using namespace std;

int main()
{
    Vector<int> tTester;
    tTester.PushBack(0);
    tTester.PushBack(1);
    tTester.PushBack(2);

    for (Vector<int>::Iterator iter = tTester.Begin(); !iter.IsEqual(tTester.End()); iter.Next())
    {
        cout << iter.GetData() << " ";
    }

    cout << endl;
    tTester.Erase(tTester.Begin());

    for (Vector<int>::Iterator iter = tTester.Begin(); !iter.IsEqual(tTester.End()); iter.Next())
    {
        cout << iter.GetData() << " ";
    }

    cout << endl;
    tTester.Insert(tTester.Begin(), 999);
    tTester.Insert(tTester.Begin(), 888);

    for (Vector<int>::Iterator iter = tTester.Begin(); !iter.IsEqual(tTester.End()); iter.Next())
    {
        cout << iter.GetData() << " ";
    }
}

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019 ver.16.0.2.

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Chironian

    2020/03/08 15:05

    呼び出し側がないと超面倒ですので、呼び出し側も提示した方が回答が付きやすいですよ。

    キャンセル

  • Chironian

    2020/03/08 15:11

    試しに、Vector<int> vec;を定義してみましたが、コンストラクタが適切に定義されていないため、エラーになります。Iteratorにまでたどり着かないです。まずは、Vectorをきちんと定義しましょう。

    キャンセル

  • do_Shiro_to

    2020/03/09 07:20

    返答ありがとうございます。
    お手数をおかけして申し訳ありません。あくまで雰囲気だけですが、呼び出し側も追加いたしました。
    具体的に、Vectorのコンストラクタをどう定義をすればIteratorにたどり着くのでしょうか…?

    キャンセル

  • Chironian

    2020/03/09 12:58

    それはまた別途質問された方が良いと思います。ただ内容的にC++の基本文法の話ですので「まるなげ」との低評価が付く可能性があります。ですので、まずはご自身でコンストラクタの書き方を調査されたほうが無用なストレスを回避できる可能性が高いです。
    http://kaitei.net/cpp/constructors/

    キャンセル

回答 2

+2

全体的におかしいのでどこがおかしいということを指摘するのが難しいのですが、ちょっとした書き損じを別にして根本的に理解を間違えているらしいのは「クラス内でクラスを定義する意味」についてです。

クラスの中で定義するクラスというのは可視性を制御するに過ぎず、データに親子関係が出来るわけではありません。 Vector のメンバである mData や Size に対して具体的なオブジェクトを経由せずに Vector::Iterator のメンバ関数内からアクセスすることはできません。 逆に Vector::Iterator のメンバである mMyVector を Vector のメンバ関数が使っているのも奇妙です。

Vector のイテレータを定義するために Vector 内で定義する必要はありません。 やりたければ Vector 内に Iterator を定義してもかまいませんが実際には無い関連性を見出してしまって混乱しているように見えるので分けて考え直した方が良いのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/09 07:37

    ご返信ありがとうございます。
    指摘をいただいたところをできるだけ直して、再アップさせていただきました。
    クラス内クラス、いわゆるインナークラスというものを自分なりに調べてみたのですが、正直自分のコードとあまり違いが無いように見えました。クラス内にクラスを定義して、スコープ演算子を利用して、呼び出す。といった簡潔な感じで説明されていたのですが、なぜ自分のコードでは呼び出せないのでしょうか…?一応呼び出し側のコードも追加で載せたので、チェックしていただけると助かります。

    キャンセル

  • 2020/03/09 09:20

    一気に良くなりました。
    根本的な間違いと言える部分は改善されていると思います。
    あとは細かな間違いがあるだけなので、これ以上の指摘は不要でしょう。
    エラーメッセージを見て個別に修正してください。
    (単純にたくさんのミスがあるのでたくさんのエラーが出ているだけです!)
    Teratail は質問に対して答えるというのがコンセプトであり、かわりにデバッグしてもらえることを期待しないでください。
    https://teratail.com/help/avoid-asking
    個別のエラーメッセージについて理解できない部分があればあらためて具体的な質問を投稿してください。

    キャンセル

+1

次のコードが理解できれば、Vector と Iterator の実装ができるのではないでしょうか?

#include <iostream>
#include <string>
using namespace std;

template <typename T>
class Vector {
    T *mData;
    int mSize;
    int mCapacity;
public:
    Vector(int n = 15) : mData(new T[n]), mSize(n), mCapacity(n) { }
    ~Vector() { delete[] mData; }
    T& operator[](int i) { return mData[i]; }
    int Size() { return mSize; }

    class Iterator {
        T *mPtr;
    public:
        Iterator(T *p = nullptr) : mPtr(p) { }
        Iterator operator++() { ++mPtr; return *this; }
        bool operator!=(const Iterator& x) { return mPtr != x.mPtr; }
        T& operator*() { return *mPtr; }
        T* operator->() { return mPtr; }
    };

    Iterator Begin() { return Iterator(mData); }
    Iterator End() { return Iterator(mData + mSize); }
};

int main()
{
    Vector<int> a;
    for (int i = 0; i < a.Size(); i++)
        a[i] = i + 1;
    for (Vector<int>::Iterator it = a.Begin(); it != a.End(); ++it)
        cout << " " << *it;
    cout << endl;

    Vector<string> b(5);
    b[0] = "a";
    for (int i = 1; i < b.Size(); i++)
        b[i] = b[i-1] + char(b[i-1][i-1] + 1);
    for (Vector<string>::Iterator it = b.Begin(); it != b.End(); ++it)
        cout << it->size() << " " << *it << endl;
}


Vector のコピーコンストラクタや代入演算子の定義は省略しました。

追記
質問のコードの Vector のコンストラクタの 15 は mSize ではなく mCapacity のデフォルト値だったんですね。
コンストラクタを次のように修正します。

    Vector(int n = 0) : mSize(n), mCapacity(n < 15 ? 15 : n) {
        mData = new T[mCapacity];
    }


さらに、main の a は、Vector<int> a(20); にします。

追記2
main が少しずつ実行できるように作っていきましょう。

#include <iostream>
using namespace std;

template<typename T>
class Vector {
    T* mData;
    int mSize;
    int mCapacity;

public:
    Vector() {
        mSize = 0; mCapacity = 15; mData = new T[mCapacity];
    }
    ~Vector() { delete[] mData; }
    void PushBack(const T& tItem) {
        if (mSize < mCapacity)
            mData[mSize++] = tItem;
        else
            cout << "to be implemented later\n";
    }

    class Iterator {
        int mCurrentIndex = 0;
        T *mMyVector = nullptr;  // not a pointer to Vector<T>

    public:  // コンストラクタは public に
        Iterator(T *tWho, int tLocation) {  // not a pointer to Vector<T>
            mMyVector = tWho;
            mCurrentIndex = tLocation;
        }
        T& GetData() { return mMyVector[mCurrentIndex]; }
        void Next() { mCurrentIndex += 1; }
        bool IsEqual(const Iterator& rhs) const {
            return mMyVector == rhs.mMyVector && mCurrentIndex == rhs.mCurrentIndex;
        }
    };  // end of class Iterator definition

    Iterator Begin() { return Iterator(mData, 0); }
    Iterator End() { return Iterator(mData, mSize); }
};

int main()
{
    Vector<int> tTester;
    tTester.PushBack(0);
    tTester.PushBack(1);
    tTester.PushBack(2);

    for (Vector<int>::Iterator iter = tTester.Begin();
            !iter.IsEqual(tTester.End()); iter.Next()) {
        cout << iter.GetData() << " ";
    }
    cout << endl;
}


IsEqual は Vector のすべての要素が等しいかどうかではなく、
Iterator が同じ要素を指しているかどうかを調べるものです。

追記3
Iterator が Vector<T> へのポインタを持つように書くこともできます。

#include <iostream>
using namespace std;

template<typename T>
class Vector {
    T* mData;
    int mSize;
    int mCapacity;

public:
    Vector() {
        mSize = 0; mCapacity = 15; mData = new T[mCapacity];
    }
    ~Vector() { delete[] mData; }
    void PushBack(const T& tItem) {
        if (mSize < mCapacity)
            mData[mSize++] = tItem;
        else
            cout << "to be implemented later\n";
    }
    T& At(int i) { return mData[i]; }

    class Iterator {
        int mCurrentIndex = 0;
        Vector<T> *mMyVector = nullptr;

    public:  // コンストラクタは public に
        Iterator(Vector<T> *tWho, int tLocation) {
            mMyVector = tWho;
            mCurrentIndex = tLocation;
        }
        T& GetData() { return mMyVector->At(mCurrentIndex); }
        void Next() { mCurrentIndex += 1; }
        bool IsEqual(const Iterator& rhs) const {
            return mMyVector == rhs.mMyVector && mCurrentIndex == rhs.mCurrentIndex;
        }
    };  // end of class Iterator definition

    Iterator Begin() { return Iterator(this, 0); }
    Iterator End() { return Iterator(this, mSize); }
};


main は省略しました。

追記4
friend の意味を誤解していませんか?
質問のコードでは、class Iterator の中に frined class Vector; がありますが、
これは、Iterator の中のメンバ mCurrentIndex や mMyVector が private であっても
Vector の中のメンバ関数から参照できるようにするものです。
そんなものが必要でしょうか?

逆に class Vector の中に、frined class Iterator; と書けば、
Iterator の中のメンバ関数が Vector の中のメンバ mData などを参照できます。

例えば、追記3 のコードでそう書けば、Vectorのメンバ関数 At がなくても、
Iterator のメンバ関数 GetData が次のように書けます。

        T& GetData() { return mMyVector->mData[mCurrentIndex]; }

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/03/09 07:26

    ご返信ありがとうございます。
    提示してくださったコードは、大体何をしているかは理解できたのですが、これでどうして動くのかはよくわかりませんでした。具体的に、私のコードとの違いを教えてもらえませんでしょうか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る