2015-10-14 17 views
9

Sagen, ich habe diese Klasse:const Methode in einer Klasse Rückkehr Vektor von Zeigern

#include <vector> 
using namespace std; 

class Bag 
{ 
    vector<int*> items; 

public: 
    void addItem(int* i) 
    { 
     items.push_back(i); 
    } 
    const vector<int*> getItems() const 
    { 
     return items; 
    } 
}; 

Das Problem ist, ich durch die Zeiger in Parameterpunkten wiesen Änderungen der Werte verhindern will. Aber mit dem von getItems zurückkehrenden Vektor darf ich die angegebenen Werte ändern.

Jetzt, um das zu beheben, kann ich die Parameter-Elemente als deklarieren, aber dann kann ich auch die Werte in meiner Klasse nicht ändern. Gibt es eine andere Möglichkeit, die Werte zu schützen, aber in den Elementdeklarationen immer noch nicht const zu verwenden?

+7

'std :: Vektor getItems() const {return std :: Vektor (items.begin(), items.end()); } ' –

+0

@KerrekSB sollte dies eine Antwort sein. –

+0

@KerrekSB Danke für die Antwort, aber ist das der einzige Weg? Ich habe das Gefühl, eine Kopie zu erstellen ist ineffizient auch, wenn ich eine Kopie erstellen muss ich nach Wert zurückgeben, richtig? auch, was sagst du zur ersten Frage? – Adam

Antwort

6

Wie @KerrekSB wies darauf hin, müssen Sie die Struktur rekonstruieren, die Art der das Element zu einem const ein Wechsel:

std::vector<const int*> getItems() const { 
    return std::vector<const int*>(items.begin(), items.end()); 
} 

Hier ist der Konstruktor von vector<const int*> nutzt die implizite Konvertierung von int* bis const int*. Sie können nicht einfach items hier zurückgeben, weil vector<T> ist invariant auf T. Dies bedeutet direkt, dass es keine Möglichkeit gibt, von vector<int*> zu vector<const int*> nur von einem Typ zu konvertieren.


Ihren Kommentar zu adressieren:

Ist dies der einzige Weg? Ich habe das Gefühl, eine Kopie zu erstellen ist ineffizient.

Wie für die Kopie kopiert es nur die Zeigerdaten, also normalerweise 8 Bytes pro Element. Für einen Vektor von Zeigern zu größeren Strukturen ist es typischerweise vernachlässigbar, aber für einen Vektor von int ist es tatsächlich ein ziemlich großer Overhead.

Was bringt uns zu der Frage: Warum speichern Sie Zeiger in erster Linie? Speichern Sie einfach vector<int>, und dann können Sie einfach einen const& zurückgeben.

+0

Vorsichtig mit diesen Zeigern, wenn die 'Bag' geht, werden sie baumeln. 'shared_ptr' ist viel besser und vermeidet Zeiger noch mehr. – edmz

+0

Der Grund, warum ich Zeiger verwendet habe, ist, weil ich später Objekte verwenden wollte, die nicht standardmäßig c'tor – Adam

+2

Code zu 'return {items.begin(), items.end()};' – Jarod42

5

Wenn Sie Kopien vermeiden wollen und lassen Sie nur lesenden Zugriff auf Ihre std::vector Elemente, könnten Sie stattdessen Funktionen bieten eine const_iterator in den Behälter zu bekommen:

class Bag 
{ 
    vector<int*> items; 

public: 
    void addItem(int* i) 
    { 
     items.push_back(i); 
    } 

    //could just be called begin if that gives the correct idea 
    vector<int*>::const_iterator itemsBegin() const 
    { 
     return std::cbegin(items); 
    } 

    vector<int*>::const_iterator itemsEnd() const 
    { 
     return std::cend(items); 
    } 
}; 

Sie dann würde Schleife über die Bag Inhalte mit dieser Iterator, anstatt eine ganze vector von Elementen. Wenn Sie die Funktionen begin und end zur Verfügung stellen, können Sie sogar bereichsbasierte For-Schleifen verwenden.

+1

Oder in C++ Als nächstes wäre das einfach ein Bereich. –

+0

@BartekBanachewicz bitte erklären. –

+1

@VictorPolevoy bezieht sich wahrscheinlich auf diese [Bereichsbibliothek] (https://github.com/ericnebler/range-v3) und den [zugehörigen Standardvorschlag] (http://www.open-std.org/jtc1/sc22/) wg21/docs/papers/2015/n4382.pdf). – TartanLlama

Verwandte Themen