C++ 在本例中,如何避免代码重复?

C++ 在本例中,如何避免代码重复?,c++,C++,我有一个简单的库(我们称之为库#1),它可以处理一些数据向量(例如时间序列)。我想创建一个新的库(library#2),它基本上具有大多数(但不是全部)相同的函数,但只作用于该向量中的单个数据点。我设想一个解决方案,它是现有库的一个薄包装,可以最大限度地减少代码重复 下面是简单的库#1: class Foo { private: std::vector<double> data; public: // Some constructors double get_

我有一个简单的库(我们称之为
库#1
),它可以处理一些数据向量(例如时间序列)。我想创建一个新的库(
library#2
),它基本上具有大多数(但不是全部)相同的函数,但只作用于该向量中的单个数据点。我设想一个解决方案,它是现有库的一个薄包装,可以最大限度地减少代码重复

下面是简单的
库#1

class Foo {
private: 
   std::vector<double> data;

public:
   // Some constructors

   double get_data_at_timepoint(int timepoint) const;

   // Some other methods
}
其中(重要
执行某些操作
调用
在某个时间点获取数据

void Bar::do_something() {
   // ...
   double x = foos[some_idx].get_data_at_timepoint(some_timepoint);
   // ...
};
我想要的
Library#2
也是
Library#1
单个时间点上。比如:

class Foo2 {
private:
    double data;

public:
    double get_data() const;

    // All those other methods of Foo
}

class Bar2 {
private:
    std::vector<Foo2> foos;

public:
    Foo2 get_this_foo_2(int idx) const;
    void do_something();
    // All those other methods of Bar
}
显然,
Foo2
基本上只是
Foo
,但只有一个数据条目。我可以重写所有的
Foo
,但是我必须复制所有的方法。相反,我想定义长度为1(单个数据点)的
Foo
薄包装

对于
Foo2
,有两个选项:(1)子类
Foo
,或(2)让
Foo2
成为
Foo
的唯一ptr的包装器。我认为(2)更好,因为用户不应该访问基类
Foo
中的时间点

我还希望避免为
Bar
编写额外的代码。当然,
dou_something
的功能需要在
Bar2
中稍加调整,但总的来说,这两个功能似乎是平行的。
Bar
中的许多其他方法也是相同的

如何避免
Foo2
Bar2
的代码重复

template <typename FooType>
class BarBase {
private:
    std::vector<FooType> foos;
protected:
    virtual double get_data_from_foo(unsigned int, void*) = 0;
public:
    void do_something();
    // all other methods that used to be in Bar
};

class Bar : public BarBase<Foo> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* time_ptr) {
        return foos[id].get_data_at_timepoint(*(timepoint_t*)time_ptr);
    }
};

class Bar2 : public BarBase<Foo2> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* dummy) {
        return foos[id].get_data();
    }
};
模板
巴巴多斯类{
私人:
std::矢量foos;
受保护的:
虚拟双精度从\u foo获取\u数据(无符号整数,void*)=0;
公众:
虚空做某事;
//以前在酒吧使用的所有其他方法
};
类酒吧:巴巴多斯公共酒吧{
受保护的:
虚拟无效从\u foo获取\u数据\u(未签名的整数id,无效*时间\u ptr){
返回foos[id]。在时间点(*(时间点t*)时间点ptr)获取数据;
}
};
第2类:公共巴巴多斯{
受保护的:
虚拟void从\u foo获取\u数据(无符号int-id,void*dummy){
返回foos[id]。获取_数据();
}
};
您必须在BarBase::do\u something()中调用get\u data\u from\u foo()。您还必须计算时间点并将其传递给该函数,而不管是否需要它


或者,如果您不介意do_something()中的代码重复,请从_foo()中删除get_data_,并向Bar和Bar2中的每一个添加一个do_something()成员函数,分别定义它们 因此,只使用成员函数来隐藏类的数据结构的实现细节,或者如果需要虚拟函数,那么对于大多数情况下希望使用自由函数的所有其他内容

实现可能是这样的(这只是一个概念验证代码,用于说明自由函数的用法):


Bar
继承并隐藏
do\u something
,使用一个新的方法执行您想要的操作。但是
get\u这个\u foo\u 2
实际上会返回一个
foo
对象,而不是
Foo2
,对吗?您可以使用Bar中当前的所有方法创建一个BarBase模板类,但是添加一个抽象的get_data_from_foo成员函数。使Bar从BarBase继承,Bar2从BarBase继承。你必须做的每一件事都是使用相应的方法实现GETXDATAYFROXFOO。这就是为什么在许多C++库中,类本身只有少量的成员函数,它们只限于描述这个类型所需要的。其他所有问题都是使用免费函数解决的,这使您能够以更好的方式模块化代码和重用功能。因此,只使用成员函数来隐藏类的数据结构的实现细节,或者如果需要虚拟函数,那么对于大多数情况下希望使用免费函数的所有其他内容,都可以使用。这太棒了,
c++
太棒了,太棒了。谢谢
void Bar2::do_something() {
   // ...
   double x = foos[some_idx].get_data();
   // ...
};
template <typename FooType>
class BarBase {
private:
    std::vector<FooType> foos;
protected:
    virtual double get_data_from_foo(unsigned int, void*) = 0;
public:
    void do_something();
    // all other methods that used to be in Bar
};

class Bar : public BarBase<Foo> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* time_ptr) {
        return foos[id].get_data_at_timepoint(*(timepoint_t*)time_ptr);
    }
};

class Bar2 : public BarBase<Foo2> {
protected:
    virtual void get_data_from_foo(unsigned int id, void* dummy) {
        return foos[id].get_data();
    }
};
#include <iostream>
#include <vector>

class Foo {
private: 
   std::vector<double> data = {1,2,3};
public:
   std::vector<double>::const_iterator begin() const {
      return data.begin();
   }

   std::vector<double>::const_iterator end() const {
      return data.end();
   }

   double first() const {
     return *begin();
   }
};

class Foo2 {
private: 
   double data = 42;

public:
   const double * begin() const {
      return &data;
   }

    const double * end() const {
      return &data + 1;
   }

   double first() const {
     return *begin();
   }
};

template<typename T>
double get_data_at_timepoint(const T &obj, size_t index) {
    auto it = obj.begin()+index;
    return *it;
}

template<typename T>
double get_data(const T &obj) {
    return obj.first();
}


int main()
{
    Foo f;
    Foo2 f2;

    double d = get_data_at_timepoint(f, 2);
    double d2 = get_data_at_timepoint(f2, 0);


    double d3 = get_data(f);
    double d4 = get_data(f2);
   // double d2 = get_data_at_timepoint(f2, 0);
    std::cout << "get_data_at_timepoint " << d << " " << d2 << std::endl;
    std::cout << "get_data " << d3 << " " << d4 << std::endl;
}
template<>
double get_data_at_timepoint<Foo>(const Foo &obj, size_t index) {
    // Foo related implementation
}