2013-04-01 2 views
1

У меня проблема с использованием шаблона декоратора. Конструкторы распечатывают адреса для отладки. Собран с:Рисунок декоратора - как НЕ вызывать конструктор копирования при инициализации?

g++ -g -o go Decorator.cpp 

Мой упрощенный код:

#include <iostream> 

class Base 
{ 
public: 
    Base() 
    { 
     std::cout << "Base created - this: " << this << std::endl; 
    } 
    virtual ~Base() {} 
}; 

class Decorator : public Base 
{ 
public: 
    Decorator(const Base & decorated) 
    : _decorated(&decorated) 
    { 
     std::cout << "Decorator created - this: " << this << " created - _decorated is " << _decorated << std::endl; 
    } 



    ~Decorator() 
    { 
     std::cout << "Decorator destroyed" << std::endl; 
     std::cout << " This: " << this << ", _decorated: " << _decorated << std::endl; 
    } 

private: 
    const Base * _decorated; 
}; 

class Inside : public Base 
{ 
public: 
    Inside() 
    { std::cout << "Inside created - this: " << this << std::endl; } 
}; 

class Outside : public Decorator 
{ 
public: 
    Outside(const Base & decorated) 
    : Decorator(decorated) 
    { 
     std::cout << "Outside created - this: " << this << std::endl; 
    } 
}; 

class Group : public Decorator 
{ 
public: 
    Group() 
    : Decorator(_outside) 
    , _outside(_inside) 
    { 
     std::cout << "Group created - this: " << this << std::endl; 
    } 

    ~Group() 
    { 
     std::cout << "Group destroyed" << std::endl; 
    } 

private: 
    Inside _inside; 
    Outside _outside; 
}; 

int main() 
{ 
    std::cout << "Hi there" << std::endl; 

    Group g1; 

    std::cout << "Done" << std::endl; 
} 

Моя проблема в группе :: Group(). Я считаю, что инициализация базовой части Decorator группы с неинициализированным _outside прекрасна - единственное, что хочет Decorator - это указатель на объект. Моя проблема в том, что Decorator (_outside), похоже, вызывает конструктор копирования, которого я НЕ хочу.

GDB благость:

Breakpoint 1, _fu0___ZSt4cout() at Decorator.cpp:63 
63   Group g1; 
(gdb) print g1 
$1 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, 
    _decorated = 0x77c34e42}, _inside = warning: can't find linker symbol for vi 
rtual table for `Inside' value 
{<Base> = { 
     _vptr.Base = 0x401a90}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58}, 
     _decorated = 0x401af6}, <No data fields>}} 

я нарушу перед конструктором g1 и написать пару _decorated членов к известным значениям, чтобы помочь отладки.

(gdb) set g1._decorated = 0 
(gdb) set g1._outside._decorated = 0xeeeeeeee 
(gdb) print g1 
$2 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, _decorated = 0x0}, 
    _inside = warning: can't find linker symbol for virtual table for `Inside' val 
ue 
{<Base> = {_vptr.Base = 0x401a90}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58}, 
     _decorated = 0xeeeeeeee}, <No data fields>}} 
(gdb) n 
Base created - this: 0x22ff34 
Inside created - this: 0x22ff34 
Base created - this: 0x22ff38 
Decorator created - this: 0x22ff38 created - _decorated is 0x22ff34 
Outside created - this: 0x22ff38 
Group created - this: 0x22ff2c 
65   std::cout << "Done" << std::endl; 
(gdb) print g1 
$3 = {<Decorator> = {<Base> = {_vptr.Base = 0x4042b8}, 
    _decorated = 0xeeeeeeee}, _inside = {<Base> = { 
     _vptr.Base = 0x4042c8}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x4042d8}, 
     _decorated = 0x22ff34}, <No data fields>}} 

После конструктора, g1._decorated имеет неинициализированную значение _outside._decorated в качестве члена _decorated, подразумевающей конструктор копирования был вызван. Если я добавлю код конструктора кода в класс Decorator:

Decorator(const Decorator & that) 
{ std::cout << "Copy constructor - this: " << this << " - that: " << &that << std::endl; } 

он действительно называет это.

Если изменить вторую строку конструктора группы из

: Decorator(_outside) 

в

: Decorator(static_cast<const Base &>(_outside)) 

и отлаживать

Breakpoint 1, _fu0___ZSt4cout() at Decorator.cpp:63 
63   Group g1; 
(gdb) print g1 
$1 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, 
    _decorated = 0x77c34e42}, _inside = warning: can't find linker symbol for vi 
rtual table for `Inside' value 
{<Base> = { 
     _vptr.Base = 0x401a90}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58}, 
     _decorated = 0x401af6}, <No data fields>}} 
(gdb) set g1._decorated = 0 
(gdb) set g1._outside._decorated = 0xeeeeeeee 
(gdb) print g1 
$2 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, _decorated = 0x0}, 
    _inside = warning: can't find linker symbol for virtual table for `Inside' val 
ue 
{<Base> = {_vptr.Base = 0x401a90}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58}, 
     _decorated = 0xeeeeeeee}, <No data fields>}} 
(gdb) n 
Base created - this: 0x22ff2c 
Decorator created - this: 0x22ff2c created - _decorated is 0x22ff38 
Base created - this: 0x22ff34 
Inside created - this: 0x22ff34 
Base created - this: 0x22ff38 
Decorator created - this: 0x22ff38 created - _decorated is 0x22ff34 
Outside created - this: 0x22ff38 
Group created - this: 0x22ff2c 
65   std::cout << "Done" << std::endl; 
(gdb) print g1 
$3 = {<Decorator> = {<Base> = {_vptr.Base = 0x4042b8}, 
    _decorated = 0x22ff38}, _inside = {<Base> = { 
     _vptr.Base = 0x4042c8}, <No data fields>}, 
    _outside = {<Decorator> = {<Base> = {_vptr.Base = 0x4042d8}, 
     _decorated = 0x22ff34}, <No data fields>}} 

Декоратор конструктор копирования не вызывается, и все появляется быть в порядке. Мне не нравится это решение, так как это требует, чтобы каждый класс downstream не забывал это делать.

Есть ли способ получить группу из Decorator с элементом Decorator и НЕ вызвать конструктор копирования?

ответ

1

Моя проблема в том, что Decorator (_outside), по-видимому, вызывает конструктор копирования, который я НЕ хочу.

Что вы ожидаете от этого?

Decorator не имеет конструктора принимая Outside так правомочные Конструкторы либо:

Decorator(const Base&) 

или неявно определенный конструктор копирования:

Decorator(const Decorator&) 

Первый вариант предполагает неявное преобразование из Outside в Base, тогда как второй включает преобразование от Outside в Decorator, что является «лучшим» преобразованием, поскольку Outside до Base Преобразование «проходит» Decorator, чтобы добраться до Base.

Как вы обнаружили, чтобы вызвать конструктор вы хотите, что нужно сделать нужные преобразования в явном виде:

Decorator(static_cast<Base&>(_outside)) 

Это необходимо, так как тип вы передаете действительноDecorator так, конечно, он предпочитает конструктор копирования.

Другим решением является добавление конструктора, который будет использоваться вместо конструктора копирования, например. шаблон, соответствующим образом ограничен:

template<typename T> 
    Decorator(const T& decorated, typename boost::enable_if<boost::is_base_of<T, Base> >* = 0) 
    : _decorated(&decorated) 
    { } 

Это будет использоваться для всего, что является производным от Base, но не является Base и не является Decorator

В C++ 11 вы можете сделать его немного чище

template<typename T, 
     typename Requires = typename std::enable_if<std::is_base_of<T, Base>::value>> 
    Decorator(const T& decorated) 
    : _decorated(&decorated) 
    { } 
+0

Я ожидал, что он «начнет» на базе и поднимется, что не имеет никакого смысла, как только я прочитаю ваше объяснение «лучшего» преобразования :) – bizaff