如何在C ++中实现方法选择器类型的模式?

时间:2010-12-27 09:01:19

标签: c++ design-patterns

以下是该方案:

void quicksort(void *data, size_t size);
void mergesort(void *data, size_t size);
void heapsort(void *data, size_t size);


size_t binary_search(void *data, size_t size, size_t key)
{
    // Usual binary search implementation
    // ...
    return 0; // just a placeholder
}

我希望用户指定用于对数据进行排序的算法,然后根据该排序算法进行排序,然后调用二进制搜索以搜索该数据中的密钥。

如何对用户实施这种强加的最低责任?

3 个答案:

答案 0 :(得分:2)

您问题的具体说明:

不要混合排序和搜索。

首先,线性搜索是O(N),而sort_and_search最多是O(N log N),即线性搜索会更快。只有在已经排序的数据上多次搜索时,二进制搜索才是算法的好选择。

其次,您注意到调用者需要控制排序算法。为什么不让呼叫者这样做:

quicksort(data,size);
size_t result = binary_search(data,size);

概括:使用你的例子的方法:

(1)Functor参数,如Naveen所示

这会使sort_and_search成为一个模板,这可能不适合某些用途。 但它是一种类似的常见模式。 这个解决方案的优点是当binary_search对functor进行很多短调用时(在这种情况下它不会),这是额外的优化可能性。缺点是如果sort_and_search本身具有较大的主体,则生成的代码量。

(2)功能指针,如roe
所示 虽然这看起来像“C-ish”,但它是一个简单,直截了当的解决方案。缺点:您无法对sort函数进行参数化(例如,通过指定如何在二进制排序中选择一个pivot)。

(3)多态性
基本上你:

定义一个抽象基类

 struct ISort
 {
    virtual void Sort(void * data, size_t size) = 0;  
    virtual ~ISortData() {}      
 }

从中继承具体实现:

struct BinarySort : public ISort { ... }
struct MergeSort  : public ISort { ... }
struct HeapSort   : public ISort { ... }

并将排序作为参数提供给sort_and_search

size_t sort_and_search(void * data, size_t size, ISort & sort);

优点/缺点:绑定通常在运行时发生。这强烈隔离了排序和搜索实现(它们可以驻留在不同的二进制文件中)。但是,呼叫开销明显大于

的情况

更多说明:(void * data, size_t size)界面

此界面不提供类型安全性。它可以工作,但编译器无法告诉您何时传递无效参数。

STL在这里应用了三个概括:

使元素类型成为模板参数:

 template <typename T>
 size_t sas(T * values, size_t count)

这提供了类型检查,并支持类型为T的重载比较运算符。

使用迭代器而不是数组:

它们弄乱了语法,但它允许算法在没有连续存储的容器上工作。此外,您可以将容器视为单个实体,而不是指针和调用者必须匹配的计数。

可选的比较函子:

包含用于比较的仿函数的可选参数。这允许在相同类型T上使用不同的排序条件。

答案 1 :(得分:1)

我建议接受一个仿函数作为binary_search的参数。用户需要编写结构并为operator()提供实现。在operator()内,他可以使用他喜欢的任何方法进行排序。这种方法与标准库中使用的方法类似。

修改

示例代码:

void quicksort(void *data, size_t size)
{
}
void mergesort(void* data, size_t size)
{
}
struct QuickSort
{
    void operator()(void* data, size_t size) const
    {
        quicksort(data,size);
    }
};
struct MergeSort
{
    void operator()(void* data, size_t size) const
    {
        mergesort(data,size);
    }
};


template<typename Functor>
size_t binary_search(void *data, size_t size, size_t key, Functor sort)
{
    sort(data, size);
    //Rest of the code
    return 0;
}

int main() 
{ 
    binary_search(NULL,0, 0, QuickSort());
    binary_search(NULL,0, 0, MergeSort());
    return 0; 
}

答案 2 :(得分:1)

如果要提供一组用于排序的函数,并在搜索前在binary_search函数中进行排序,请尝试以下操作:

void quicksort(void *data, size_t size);
void mergesort(void *data, size_t size);
void heapsort(void *data, size_t size);

size_t binary_search(void *data, size_t size, size_t key, void(*algorithm)(void*,size_t) = &quicksort)
{
    alogrithm(data,size); // sort it..
    // Usual binary search implementation
    return 0; // just a placeholder
}

这是一个C风格的解决方案,添加了C ++的默认参数,让用户根本不指定任何内容。要使用更多C ++,您的算法应该是Naveen建议的算子,并让用户指定其中一个。虽然这将消耗更多内存并导致一个间接(和缓存未命中),因为'operator()'必然是虚拟的,但这可能是微不足道的。或者你可以模板化,在这种情况下你将只使用更多的代码。仿函数具有额外的好处,您可以参数化算法(例如,升序或降序)。

也就是说,感觉这是一种奇怪的方式,让'binary_search'进行排序。我强加了数据必须排序的限制,因为排序已排序的数据是浪费时间(例如,如果您进行多次搜索)。此外,快速排序在排序数据上趋于O(n ^ 2)。