3

クラスのプライベートメンバーであるVタイプのベクトルが呼び出されたとします。vector<int>

クラスのこのパブリック関数もあります。

 vector<int> getV(){ return V; }

このクラスのインスタンスがあり、値を読み取ってベクトル内のすべての値の合計を見つけるだけでよい場合、

次のように言えます。

 MyClass obj;
 //update vector
 size_t i, size;
 size = obj.getV().size();
 int sum = 0;
 for(size_t i = 0; i < size; i++){
     sum += obj.getV().at(i);
 }

または、次のように言うこともできます。

  MyClass obj;
 //update vector
 size_t i, size;
 vector<int> myV = obj.getV();
 size = myV.size();
 int sum = 0;
 for(size_t i = 0; i < size; i++){
     sum += myV[i];
 }

2 番目のケースでは、ベクトル全体を vector にコピーしますmyV。ただし、最初のケースで正確に何が起こるかはわかりません。ベクトルをそのまま使用しますか、それとも関数を呼び出すたびに実際にベクトルをコピーしますgetV()か?

コピーが発生しない場合は、最初のバージョンの方が効率的だと思います。

しかし、私は正確に何が起こっているのか 100% ではありません。

vector への参照を返せば、コピーをまったく避けることができると思いVます。したがって、次の関数を使用できます。

vector<int>* getV { return &V; }

その後

 MyClass obj;
 //update vector
 size_t i, size;
 vector<int> * myV = obj.getV();
 size = myV->size();
 int sum = 0;
 for(size_t i = 0; i < size; i++){
     sum += myV->at(i);
 }

ただし、最初のケースで正確に何が起こっているのか知りたいです。コピーされているものはありますか?3 番目のケースでもポインタを返すため、何らかのコピーが発生しています。

前もって感謝します

4

5 に答える 5

1

最初のケースは、ベクトルを数回コピーできるため、非常に悪いです。コンパイラはコードを最適化 (または最適化しない) し、この問題を隠します (使用するコンパイラによって異なります)。最善の解決策は、次のような const 参照を返すメソッドを定義することです

const std::vector<int> & getV() const { return V; }

次のコードを使用します

const vector<int> & myV = obj.getV();
int sum = 0;
for(size_t i = 0, size = myV.size(); i < size; ++i){
 sum += myV[i];
}

ちなみに、ベクトルを合計するコードは次のように置き換えることができます。

int sum = std::accumulate(myV.begin(), myV.end(), 0);
于 2013-05-08T08:12:11.007 に答える
1

コンパイラの最適化の可能性を考慮していないため、最初のバージョンでは、反復ごとにベクター全体のコピーが戻り値として作成されます。これは非常に非効率的です。

V はクラス メンバーであり、独立した変数ではないため、ここではRVOは可能ではないと思います。

何が起こっているかの例を次に示します3 要素のベクトルのトレーサー出力から。

starting loop

[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer& Tracer::operator=(const Tracer&)
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()

[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer& Tracer::operator=(const Tracer&)
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()

[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer::Tracer(const Tracer&)
[(none)]    Tracer& Tracer::operator=(const Tracer&)
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()
[(none)]    Tracer::~Tracer()

Ending loop

ご覧のとおり、ベクター全体 (3 つの要素) が反復ごとにコピーされます。

-------------------------------------------------- -------------------------------------------

はるかに優れた実装は、ベクトルへの参照を返すことです。

 vector<int>& getV(){ return V; }
           ^^^

これで、コピーを作成しなくなります。 これが、このバージョンで何が起こるかです。そして、これがトレーサーからの出力です。

starting loop
[(none)]    Tracer& Tracer::operator=(const Tracer&)
[(none)]    Tracer& Tracer::operator=(const Tracer&)
[(none)]    Tracer& Tracer::operator=(const Tracer&)
Ending loop
于 2013-05-08T08:12:25.383 に答える
-1

継承によってクラスを拡張する方法については、MyClass ですべての STL アルゴリズムを使用できます。MyClass を Sequence Container の拡張として定義した後、Sequence パブリック インターフェイスを継承すると、オブジェクトを STL アルゴリズムで操作できます。独自のループを作成することは問題ありませんが、STL を最大限に使用すると、より読みやすく保守しやすいコードが得られます。効率を確保するためにアルゴリズムを操作する場合にのみ注意する必要があります (たとえば、範囲メンバー関数と単一要素関数の使用など)。 .

#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
#include <numeric>

template
<
    typename Type, 
    template
    <
        typename Element, 
        typename Allocator=std::allocator<Element>
    > class Sequence
>
class MyClass
:
    public Sequence<Type>
{
    public: 

        MyClass()
            :
                Sequence<Type>()
        {}

        template<typename Iterator>
        MyClass(Iterator begin, Iterator end)
        :
            Sequence<Type>(begin, end)
        {}
};

template<typename Type>
class add_element 
{
    Type const& t_; 

    public: 

        add_element(Type const& t)
            :
                t_(t)
        {}

        template<typename Element>
        void operator()(Element & lhs)
        {
            lhs += t_; 
        }
};

using namespace std;

int main(int argc, const char *argv[])
{
    MyClass<int, vector> m;

    m.push_back(0);
    m.push_back(1);
    m.push_back(2);
    m.push_back(3);

    copy(m.begin(), m.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    for_each(m.begin(), m.end(), add_element<int>(-10));

    copy(m.begin(), m.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    MyClass<int,vector>::value_type sum = accumulate(m.begin(), m.end(), 0); 

    cout << "sum = " << sum << endl;


    return 0;
}

出力:

0 1 2 3 
-10 -9 -8 -7 
sum = -34

同様に、std::accumulate を使用して要素の合計を計算したり、std::sort を使用して MyObject を並べ替えたりすることができるようになりました。

于 2013-05-08T08:11:44.753 に答える