2016-12-08 9 views
1

Я пытаюсь написать два класса и B, где B содержит экземпляр класса A. На практике A содержит некоторые большие данные. В этом игрушечном примере, давайте предположим, что они являются следующие:C++ инициализация объекта, который содержит другую проблему проектирования объекта

class A{ 
public: 
    int a; 
    A(){} 
    A(const A& ac){ 
     cout <<"copy constructor for A" << endl; 
     a = ac.a; 
    } 
    virtual ~A(){} 
    A& operator=(const A& that){ 
     cout <<"operator = for A" << endl; 
     a = that.a; 
     return *this; 
    } 
}; 
class B{ 
private: 
    A ba; 
public: 
    B(){ a = A(); } 
    B(A& a){ 
     cout << "Using default constructor" << endl; 
     ba = a; 
    } 
    B(const B& copy){ 
     cout<<"copy"<< endl; ba = copy.ba; 
    } 
    B& operator=(const B& that){ 
     cout<<"invoking operator = "<< k<< endl; 
     ba = that.ba; 
     return *this; 
    } 
    virtual ~B(){} 
    A& getA(){ return ba; } 
    void printAadrr(){ 
     cout << &ba <<" "<< ba.a<< endl; 
    } 
}; 

Проблема в том, что при инициализации B с помощью B::B(A& a), аргумент копируется в экземпляр ba в B. Это может быть неэффективно, если a велико.

я подумал о другом подходе, где вместо

class B{ 
private: 
    A ba; 
    .... 

я мог бы сделать

class B{ 
    private: 
     A* ba; 
public: 
    B(){ 
     ba = new A(); //but we have to free memory in destructor if we call default constructor 
    } 
    B(A& a){ 
     cout << "Using default constructor" << endl; 
     ba = &a; 
    } 

Но этот дизайн выбор имеет свой собственный вопрос, где, если я делаю B конструктором по умолчанию, мне нужно освободить память, выделенная для ba позже в деструкторе.

Есть удобный способ передать «ссылку» объекта A (создано за пределами B) в B в конструкторе, если мы используем B::B(A& a) и создать пустой, по умолчанию объект A если мы будем использовать B :: B ()? Я пробовал

B::B(A& a){ 
    &ba = &a; 
} 

но он дает ошибку «lvalue должен быть слева от задания».

Любые советы приветствуются. Извините за слишком длинный пост.

+0

C++ 11 предоставляет ссылки на _rvalue_ вместе с 'std :: move', чтобы ваши намерения были четкими. Это полезно, если вы хотите _transfer_ 'A' в' B'. В противном случае вы можете хранить ссылку на внешний объект. Но вы должны использовать списки инициализаторов в конструкторе, а не прямое назначение. Или вы можете использовать 'std :: reference_wrapper'. Остерегайтесь хранения ссылок в объектах, потому что у вас могут быть проблемы с продолжительностью жизни объекта. – paddy

+0

uhmn ... спасибо за «указатель». Я не знал о 'std :: move' или' std :: reference_wrapper'. Я прочитаю об этом. – TuanDT

ответ

2

Определить конструктор, используя перемещение семантику следующим образом:

B::B(A && a) 
: ba(std::move(a)) 
{} 

Теперь вы можете передать экземпляр в объект.

A a; 
B b(std::move(a)); 
// `b` stores content of `a`, and local `a` is no longer valid. 

Вы все еще можно построить с помощью конструктора копирования, если вы хотите .

A a; 
B b(a); 
// `b` stores a copy of `a`, and local `a` is still valid. 
+0

ahhh awesome! это то, что я ищу – TuanDT

+0

Я только что исправил свой ответ. Это не был конструктор движений. Конструктор перемещения был бы «B :: B (B && b)». – paddy

+0

я вижу. Я только что начал читать эти семантические движения. Я довольно новичок в C++ 11 – TuanDT

1

Вместо того, чтобы иметь как простой вар члена, есть указатель на А. Тогда при построении B, просто передать указатель на экземпляр А.

class B{ 
private: 
    A* ba; 
+0

Спасибо, но это мой второй подход, который имеет свою проблему, как указано в вопросе – TuanDT

+0

. В этом подходе, поскольку у вас есть указатель на A в B, вы * должны * НЕ создавать объект A изнутри B. Целый точка указателя состоит в том, что он может содержать ссылку на что-то в памяти, которое уже создано другой частью программы. В этом случае A уже был бы создан в основном методе. Я не уверен, что это соответствует вашему дизайну. –

+0

Да, 'A' должен быть инициирован в основном методе, но в случае конструктора по умолчанию у меня не будет готового объекта' A' в основном методе, поэтому мне пришлось искать * надежный * подход сделать 'B' независимо от того, откуда приходит« A ». – TuanDT

1

это зависит от вашего дизайна. Если вы хотите, чтобы B использовали A, используйте std::move для получения A по B.

В общем случае используйте std::shared_ptr, который будет управлять памятью самостоятельно. После того, как все экземпляры std::shared_ptr<A> будут уничтожены, то данные A также будут удалены. Но в этом случае B экземпляры могут доля такие же A данные.

Другое решение std::unique_ptr, но вы должны переместить его каждый раз, когда вы копируете B, что означает, что право собственности A будут переданы так же, как std::move решения.