我在尝试使用装饰器模式时遇到问题。构造函数正在打印出用于调试的地址。编译:
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 :: Group()中。我相信使用未初始化的_outside初始化Group的Decorator基础部分很好 - 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; }
实际上它确实称之为。
如果我从
更改Group构造函数的第二行: Decorator(_outside)
到
: Decorator(static_cast<const Base &>(_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>}}
(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>}}
没有调用Decorator复制构造函数,所有看起来都很好。我不喜欢这个解决方案,因为它要求下游的每个班级都要记住这样做。
有没有办法从Decorator中使用成员Decorator派生Group而不调用复制构造函数?
答案 0 :(得分: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)
{ }