Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Przenoszenie unikalnych wskaźników pomiędzy kontenerami z użyciem konstruktora kopiującego

Ostatnio zmodyfikowano 2018-04-18 20:58
Autor Wiadomość
NiNJAxFREEZu
Temat założony przez niniejszego użytkownika
Przenoszenie unikalnych wskaźników pomiędzy kontenerami z użyciem konstruktora kopiującego
» 2018-04-18 20:34:17
Witam wszystkich. Mam pewien problem natury obiektowo-wskaźnikowej. Za pomocą unique pointerów (i koniecznie korzystając z konstruktora przenoszącego) przenieść z jednego kontenera unique pointerów do typu klasowego do drugiego oczywiście bez kopiowania.
Kod prezentuję się tak:

C/C++
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class Parent
{
public:
    int a;
    int b;
   
    Parent() { }
   
    Parent( int a, int b )
    {
        this->a = a;
        this->b = b;
    }
   
    Parent( const Parent & copy )
    {
        this->a = copy.a;
        this->b = copy.b;
        std::cout << "Konstruktor kopiujacy -> Parent\n";
    }
   
    Parent( Parent && p )
        : a( std::move( p.a ) )
         , b( std::move( p.b ) )
    { std::cout << "Konstruktor przenoszacy -> Parent\n"; }
   
    virtual int getSum() { return a + b; }
   
};

class Sub
    : public Parent
{
public:
    int c;
   
    Sub() { }
   
    Sub( int a, int b, int c )
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }
   
    Sub( const Sub & copy )
    {
        this->a = copy.a;
        this->b = copy.b;
        this->c = copy.c;
        std::cout << "Konstruktor kopiujacy -> Sub\n";
    }
   
    Sub( Sub && s )
    {
        Sub::a = std::move( s.a );
        Sub::b = std::move( s.b );
        Sub::c = std::move( s.c );
        std::cout << "Konstruktor przenoszacy -> Sub\n";
    }
   
    int getSum() { return a + b + c; }
};

using namespace std;
typedef std::vector < std::unique_ptr < Parent >> Kontener;

int main()
{
    Kontener kontener1, kontener2;
   
    kontener1.push_back( make_unique < Parent >( 1, 1 ) );
    kontener1.push_back( make_unique < Parent >( 2, 2 ) );
    kontener1.push_back( make_unique < Sub >( 1, 1, 1 ) );
    kontener1.push_back( make_unique < Sub >( 2, 2, 2 ) );
   
    for( int i = 0; i < kontener1.size(); ++i )
         cout << kontener1[ i ]->getSum() << endl;
   
    system( "pause" );
    system( "cls" );
   
    kontener2.push_back( make_unique < Sub >( move( kontener1[ 2 ] ) ) ); // ???????????????
   
    system( "pause" );
    return 0;
}

Sytuacja wygląda tak. Gdyby zamiast linijki oznaczonej gęstymi znakami zapytania w komentarzu umieścić w nawiasach push_back() samo "move(...)" to rzeczywiście w drugim kontenerze pojawi się nasz wskaźnik. Ale nie wywoła się konstruktor przenoszący ani kopiujący oraz próba odczytu danych z kontenera1 wywala wyjątek w momencie, gdy pętla dojdzie do wartości usunietej(?), oczywiście zwykły if == nullptr załatwia sprawę, ale dalej żadnego echa od konstruktora przenoszącego. Więc moje pytanie brzmi -> Jak wywołać przy przenoszeniu konstruktor przenoszący?
P-170738
RazzorFlame
» 2018-04-18 20:58:15
Gdyby (...) umieścić w nawiasach push_back() samo "move(...)" to rzeczywiście w drugim kontenerze pojawi się nasz wskaźnik. Ale nie wywoła się konstruktor przenoszący ani kopiujący

Konstruktor przenoszący jest wywoływany, ale nie dla klasy Parent czy Sub tylko dla std::unique_ptr. Unique pointer właśnie tak jest skonstruowany, że nie można go skopiować, ale można przenieść.
std::unique_ptr może, w uproszczeniu wyglądać jakoś tak:
C/C++
template < typename T >
class unique_ptr
{
public:
    unique_ptr( T * ptr )
        : m_ptr( ptr )
    {
    }
   
    unique_ptr( unique_ptr && other )
        : m_ptr( other.m_ptr )
    {
        other.m_ptr = nullptr; // stary wlasciciel juz nim nie jest
    }
   
    unique_ptr( const unique_ptr & other ) = delete; // kopiowanie zabronione
   
    // operatory przypisania przez przenoszenie i kopiowanie analogicznie do ^^^
private:
    T * m_ptr;
};
Kiedy wywołujesz std::move na obiekcie typu std::unique_ptr to pozwalasz innemu unique pointerowi zabrać sobie wskaźnik, którego tamten był właścicielem. Wywołuje się konstruktor przenoszący, ale dla std::unique_ptr.

oraz próba odczytu danych z kontenera1 wywala wyjątek w momencie, gdy pętla dojdzie do wartości usunietej(?)
Kiedy używasz semantyki przeniesienia, to po wykonaniu przeniesienia zawartości z A do B nie wolno Ci traktować A tak, jakby wciąż posiadał poprawną wartość. Najprawdopodobniej w std::unique_ptr dzieje się to, co pokazałem u góry w kodzie - w momencie przeniesienia, m_ptr dawnego właściciela staje się nullptr przez co potem próbujesz odwołać się do adresu zerowego, co skutkuje crashem.

Jak wywołać przy przenoszeniu konstruktor przenoszący?
Przeprojektuj swój kod, bo troche źle zrozumiałeś ideę przenoszenia i unique pointerów.
P-170739
« 1 »
  Strona 1 z 1