我有一个简单的库(我们叫它Library #1
),它可以处理某些数据向量(例如时间序列)。我想创建一个新的库(Library #2
),它实际上具有大多数(但不是全部)相同的功能,但仅作用于该向量中的单个数据点。我想象一种解决方案,它是现有库的一个瘦包装,可以最大程度地减少代码重复。
这是简单的Library #1
:
class Foo {
private:
std::vector<double> data;
public:
// Some constructors
double get_data_at_timepoint(int timepoint) const;
// Some other methods
}
get_data_at_timepoint
仅返回数据向量的适当元素(假设它存在)。库Bar
中的另一个类有一个Foo
容器,并以某种方式对其进行操作-特别是,它可以do_something
,并且还可以得到一个Foo
:
class Bar {
private:
std::vector<Foo> foos;
public:
// Some constructors
Foo get_this_foo(int idx) const;
void do_something();
// Some other methods
}
其中(重要)do_something
以某种方式调用get_data_at_timepoint
:
void Bar::do_something() {
// ...
double x = foos[some_idx].get_data_at_timepoint(some_timepoint);
// ...
};
我还想在{strong>单个时间点使用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
}
很显然,void Bar2::do_something() {
// ...
double x = foos[some_idx].get_data();
// ...
};
基本上只是Foo2
,但只有一个数据条目。我可以重写Foo
的所有内容,但随后必须重复所有方法。我想改为定义长度为1(单个数据点)的Foo
的精简包装。
对于Foo
,有两个选择:(1)子类Foo2
,或(2)使Foo
成为Foo2
唯一ptr的包装。我认为(2)更好,因为用户不应访问例如基类Foo
中的时间点。
我还想避免为Foo
编写额外的代码。当然,Bar
中的功能do_something
需要稍作调整,但总体而言,这两个看起来是如此并行。 Bar2
中的许多其他方法也相同。
如何避免Bar
和Foo2
的代码重复?
答案 0 :(得分:1)
执行以下操作:
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();
}
};
您必须在BarBase :: do_something()中调用get_data_from_foo()。无论是否需要,您还必须计算时间点并将其传递给该函数。
或者,如果您不介意do_something()内部的代码重复,则删除get_data_from_foo()并向Bar和Bar2中的每个添加do_something()成员函数,分别定义它们。
答案 1 :(得分:0)
这就是为什么在许多c ++库中,类本身仅具有少量成员函数的原因,这些成员函数仅限于描述类型所真正需要的成员函数。使用免费功能可以解决所有其他问题,这使您可以对代码进行模块化并以更好的方式重用功能。
因此,仅使用成员函数来隐藏类的数据结构的实现详细信息,或者如果需要使用virtual,则对于大多数其他情况,您大多数时候都希望使用自由函数。
实现可能看起来像这样(这只是概念证明代码,以说明自由函数的用法):
//INSERT: This occurs when the user clicks enter and a random code is sent to the database and the user's email and/or phone
$insert = mysqli_query($con, "INSERT INTO activity (id, usr, code, status) VALUES ('{$UNIQUEID}', '$userid', '0314f5s4f14', 0)") ? null : die();
//UPDATE: This occurs when the user clicks the link in their text messages or email which sets the code to used status.
$update = mysqli_query($con, "UPDATE verification SET status='1' WHERE
id='".$_SESSION['id']."'") ? null : die();
//HINT: use json for client to server applications, research progressive web apps.
$data = array('_POST' => $_POST, '_SESSION' => $_SESSION, 'sql' => $sql, 'dlt' => $dltq);
print json_encode($data); unset($data); mysqli_close($con);
现在,您可以将#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;
}
与支持get_data_at_timepoint
类型的迭代器的任何数据类型一起使用,而不仅限于您的double
类。
如果Foo
对于这些类之一来说是特殊的,则可以为该类创建专门的版本:
get_data_at_timepoint