CRTP hoặc tính đa hình thời gian biên dịch là khi bạn biết tất cả các loại của mình tại thời gian biên dịch. Miễn là bạn đang sử dụng addWidget
để thu thập danh sách các tiện ích khi chạy và miễn là fooAll
và barAll
thì phải xử lý các thành viên của danh sách tiện ích đồng nhất đó khi chạy, bạn phải có khả năng xử lý các loại khác nhau khi chạy. Vì vậy, đối với vấn đề bạn đã trình bày, tôi nghĩ rằng bạn đang bị mắc kẹt bằng cách sử dụng đa hình thời gian chạy.
Một câu trả lời tiêu chuẩn, tất nhiên, là để xác minh rằng hiệu suất của đa hình thời gian chạy là một vấn đề trước khi bạn cố gắng tránh nó ...
Nếu bạn thực sự cần phải tránh polymorpism chạy, sau đó một trong những cách sau các giải pháp có thể hoạt động.
Lựa chọn 1: Sử dụng một bộ sưu tập thời gian biên dịch của widget
Nếu các thành viên của WidgetCollection của bạn được biết tại thời gian biên dịch, sau đó bạn có thể dễ dàng sử dụng các mẫu.
template<typename F>
void WidgetCollection(F functor)
{
functor(widgetA);
functor(widgetB);
functor(widgetC);
}
// Make Foo a functor that's specialized as needed, then...
void FooAll()
{
WidgetCollection(Foo);
}
Lựa chọn 2: Thay thế đa hình runtime với chức năng miễn phí
class AbstractWidget {
public:
virtual AbstractWidget() {}
// (other virtual methods)
};
class WidgetCollection {
private:
vector<AbstractWidget*> defaultFooableWidgets;
vector<AbstractWidget*> customFooableWidgets1;
vector<AbstractWidget*> customFooableWidgets2;
public:
void addWidget(AbstractWidget* widget) {
// decide which FooableWidgets list to push widget onto
}
void fooAll() {
for (unsigned int i = 0; i < defaultFooableWidgets.size(); i++) {
defaultFoo(defaultFooableWidgets[i]);
}
for (unsigned int i = 0; i < customFooableWidgets1.size(); i++) {
customFoo1(customFooableWidgets1[i]);
}
for (unsigned int i = 0; i < customFooableWidgets2.size(); i++) {
customFoo2(customFooableWidgets2[i]);
}
}
};
Ugly, và thực sự không OO. Các mẫu có thể giúp bằng cách giảm nhu cầu liệt kê mọi trường hợp đặc biệt; thử một cái gì đó như sau (hoàn toàn chưa được kiểm tra), nhưng bạn đang trở lại không có nội tuyến trong trường hợp này.
class AbstractWidget {
public:
virtual AbstractWidget() {}
};
class WidgetCollection {
private:
map<void(AbstractWidget*), vector<AbstractWidget*> > fooWidgets;
public:
template<typename T>
void addWidget(T* widget) {
fooWidgets[TemplateSpecializationFunctionGivingWhichFooToUse<widget>()].push_back(widget);
}
void fooAll() {
for (map<void(AbstractWidget*), vector<AbstractWidget*> >::const_iterator i = fooWidgets.begin(); i != fooWidgets.end(); i++) {
for (unsigned int j = 0; j < i->second.size(); j++) {
(*i->first)(i->second[j]);
}
}
}
};
Lựa chọn 3: Loại bỏ OO
OO là hữu ích vì nó giúp quản lý phức tạp và vì nó giúp duy trì sự ổn định khi đối mặt với sự thay đổi. Đối với các trường hợp bạn dường như mô tả - hàng nghìn tiện ích, hành vi của họ thường không thay đổi và phương pháp thành viên của họ rất đơn giản - bạn có thể không có nhiều phức tạp hoặc thay đổi để quản lý. Nếu đó là trường hợp, thì bạn có thể không cần OO.
Giải pháp này giống như đa hình thời gian chạy, ngoại trừ việc bạn duy trì danh sách tĩnh các phương thức "ảo" và các lớp con đã biết (không phải là OO) và cho phép bạn thay thế các cuộc gọi hàm ảo bằng bảng nhảy chức năng nội tuyến.
class AbstractWidget {
public:
enum WidgetType { CONCRETE_1, CONCRETE_2 };
WidgetType type;
};
class WidgetCollection {
private:
vector<AbstractWidget*> mWidgets;
public:
void addWidget(AbstractWidget* widget) {
widgets.push_back(widget);
}
void fooAll() {
for (unsigned int i = 0; i < widgets.size(); i++) {
switch(widgets[i]->type) {
// insert handling (such as calls to inline free functions) here
}
}
}
};
Chỉ cần một nit nhỏ - constructors trong C++ không thể là ảo :) –
oops, xin lỗi, cố định –
Bạn có thể thay đổi các lớp cơ sở của bạn "AbstractWidget" "WidgetCollection", hoặc được thiết kế bởi một nhà phát triển/cộng đồng? – umlcat