một số sử dụng thông minh của thừa kế ảo có thể cho phép CloneableMixin :: clone để ghi đè lên cơ sở :: bản sao?
bạn CloneableMixin<Base,Derived>
không thể ghi đè lên bất kỳ phương pháp Base
- hoặc polymorphically hoặc bằng cách lẩn trốn - vì CloneableMixin<Base,Derived>
là không bắt nguồn từ Base
.
Mặt khác, nếu CloneableMixin<Base,Derived>
là có nguồn gốc từ Base
bạn sẽ không còn có bất kỳ nhu cầu cho nó trở thành một mixin, bởi vì -
class Derived : public CloneableMixin<Base,Derived> {....};
sẽ kế thừa Base
.
Vì vậy, đối với nhu cầu của ví dụ của bạn là giải pháp được minh họa ở đây là đủ:
#include <iostream>
// cloner v1.0
template <class Base, class Derived>
struct cloner : Base
{
Base *clone() const override {
return new Derived(dynamic_cast<const Derived &>(*this));
}
~cloner() override {};
};
struct Base
{
virtual Base * clone() const = 0;
Base() {
std::cout << "Base()" << std::endl;
}
virtual ~Base() {
std::cout << "~Base()" << std::endl;
}
};
struct A : cloner<Base,A>
{
A() {
std::cout << "A()" << std::endl;
}
~A() override {
std::cout << "~A()" << std::endl;
}
};
int main()
{
A a;
Base * pb = a.clone();
delete pb;
}
(Nếu bạn đang biên soạn theo tiêu chuẩn C++ 03 chứ không phải là C++ 11, sau đó bạn có thể chỉ cần xóa sự xuất hiện của từ khóa override
.)
Giải pháp này sẽ phá vỡ một số phân cấp lớp thực tế hơn, ví dụtrong minh họa này của Template Method Pattern:
#include <iostream>
#include <memory>
using namespace std;
// cloner v1.0
template<class B, class D>
struct cloner : B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
/* Abstract base class `abstract` keeps the state for all derivatives
and has some pure virtual methods. It has some non-default
constructors.
*/
struct abstract
{
virtual ~abstract() {
cout << "~abstract()" << endl;
}
int get_state() const {
return _state;
}
void run() {
cout << "abstract::run()" << endl;
a_root_method();
another_root_method();
}
virtual void a_root_method() = 0;
virtual void another_root_method() = 0;
virtual abstract * clone() const = 0;
protected:
abstract()
: _state(0) {
cout << "abstract(): state = " << get_state() << endl;
}
explicit abstract(int state) : _state(state) {
cout << "abstract(" << state << ") : state = "
<< get_state() << endl;
}
int _state;
};
/* Concrete class `concrete` inherits `abstract`
and implements the pure virtual methods.
It echoes the constructors of `abstract`. Since `concrete`
is concrete, it requires cloneability.
*/
struct concrete : cloner<abstract,concrete>
{
concrete() {
cout << "concrete(): state = " << get_state() << endl;
}
explicit concrete(int state) : abstract(state) { //<- Barf!
cout << "concrete(" << state << ") : state = "
<< get_state() << endl;
}
~concrete() override {
cout << "~concrete()" << endl;
}
void a_root_method() override {
++_state;
cout << "concrete::a_root_method() : state = "
<< get_state() << endl;
}
void another_root_method() override {
--_state;
cout << "concrete::another_root_method() : state = "
<< get_state() << endl;
}
};
int main(int argc, char **argv)
{
concrete c1;
unique_ptr<abstract> pr(new concrete(c1));
pr->a_root_method();
pr->another_root_method();
unique_ptr<abstract> pr1(pr->clone());
pr1->a_root_method();
return 0;
}
Khi chúng tôi cố gắng để xây dựng này, trình biên dịch sẽ đưa ra một lỗi ở khởi abstract(state)
trong constuctor của concrete
(tại Barf!
bình luận), nói:
error: type 'abstract' is not a direct or virtual base of 'concrete'
hoặc các từ có hiệu lực đó. Thật vậy, cơ sở trực tiếp của concrete
không phải là abstract
nhưng cloner<abstract,concrete>
. Tuy nhiên, chúng ta không thể viết lại các nhà xây dựng như:
/*Plan B*/ explicit concrete(int state) : cloner<abstract,concrete>(state){....}
Bởi vì có được không có constructor như
cloner<abstract,concrete>::cloner<abstract,concrete>(int)
Nhưng của trình biên dịch chẩn đoán cho thấy một sửa chữa. Điều này là ảo kế thừa có thể hữu ích. Chúng ta cần abstract
để trở thành một cơ sở ảo của concrete
, mà nghĩa một cách hiệu quả "một cơ sở trực tiếp danh dự của concrete
", và chúng ta có thể đạt được điều đó chỉ bằng cách làm cho B
cơ sở ảo của cloner<B,D>
:
// cloner v1.1
template<class B, class D>
struct cloner : virtual B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
};
Cùng với đó, chúng tôi có một xây dựng sạch và đầu ra:
abstract(): state = 0
concrete(): state = 0
concrete::a_root_method() : state = 1
concrete::another_root_method() : state = 0
concrete::a_root_method() : state = 1
~concrete()
~abstract()
~concrete()
~abstract()
~concrete()
~abstract()
có nhiều lý do tốt để cảnh giác với o thừa kế ảo n nguyên tắc và để dự trữ việc sử dụng ít nhất cho các trường hợp trong đó có kiến trúc lý do - không phải để giải quyết sự cố, vì chúng tôi đã sử dụng nó ngay bây giờ.
Nếu chúng ta thích làm mà không thừa kế ảo cho vấn đề này, sau đó chúng tôi phải bằng cách nào đó đảm bảo rằng có một constructor của cloner<B,D>
rằng vang bất kỳ constuctor của B
, cho tùy ý B
. Sau đó, bất kỳ phương thức khởi tạo tương ứng nào của D
sẽ có thể khởi tạo cơ sở trực tiếp cloner<B,D>
bất kỳ đối số nào.
Đây là một Pipedream cho C++ 03, nhưng với sự kỳ diệu của mẫu variadic tham số trong C++ 11 nó rất dễ dàng:
// cloner v1.2
template<class B, class D>
struct cloner : B
{
B *clone() const override {
return new D(dynamic_cast<D const&>(*this));
}
~cloner() override {}
// "All purpose constructor"
template<typename... Args>
explicit cloner(Args... args)
: B(args...){}
};
Với điều này, chúng ta có thể viết lại constructor concrete
như /*Plan B*/
và một lần nữa chúng tôi có bản dựng và thực thi chính xác.
Bản sao có thể có của http://stackoverflow.com/questions/9422760/inheritance-in-curiously-recurring-template-pattern-polymorphic-copy-c – TemplateRex
'Base :: clone' cần phải là' virtual' theo thứ tự cho '= 0' hợp lệ. – TemplateRex
@rhalbersma Tôi không chắc nó có giống nhau hay không. Không phải tất cả các lớp cloneable của tôi đều có cùng loại cơ sở (ví dụ: 'Base',' Base2'). – user