我想要关于缓存由两个派生类共享的计算的方法的建议。作为说明,我有两种类型的归一化向量 L1 和 L2,它们各自定义了自己的归一化常数(注意:我从std::vector
这里继承的良好实践作为一个快速说明——信不信由你,我真正的问题不是关于 L1 和 L2 向量!):
#include <vector>
#include <iostream>
#include <iterator>
#include <math.h>
struct NormalizedVector : public std::vector<double> {
NormalizedVector(std::initializer_list<double> init_list):
std::vector<double>(init_list) { }
double get_value(int i) const {
return (*this)[i] / get_normalization_constant();
}
virtual double get_normalization_constant() const = 0;
};
struct L1Vector : public NormalizedVector {
L1Vector(std::initializer_list<double> init_list):
NormalizedVector(init_list) { }
double get_normalization_constant() const {
double tot = 0.0;
for (int k=0; k<size(); ++k)
tot += (*this)[k];
return tot;
}
};
struct L2Vector : public NormalizedVector {
L2Vector(std::initializer_list<double> init_list):
NormalizedVector(init_list) { }
double get_normalization_constant() const {
double tot = 0.0;
for (int k=0; k<size(); ++k) {
double val = (*this)[k];
tot += val * val;
}
return sqrt(tot);
}
};
int main() {
L1Vector vec{0.25, 0.5, 1.0};
std::cout << "L1 ";
for (int k=0; k<vec.size(); ++k)
std::cout << vec.get_value(k) << " ";
std::cout << std::endl;
std::cout << "L2 ";
L2Vector vec2{0.25, 0.5, 1.0};
for (int k=0; k<vec2.size(); ++k)
std::cout << vec2.get_value(k) << " ";
std::cout << std::endl;
return 0;
}
对于大型向量,此代码不必要地慢,因为它get_normalization_constant()
重复调用,即使它在构造后没有更改(假设修饰符 likepush_back
已适当禁用)。
如果我只考虑一种形式的标准化,我会简单地使用一个双精度值来缓存这个结果:
struct NormalizedVector : public std::vector<double> {
NormalizedVector(std::initializer_list<double> init_list):
std::vector<double>(init_list) {
normalization_constant = get_normalization_constant();
}
double get_value(int i) const {
return (*this)[i] / normalization_constant;
}
virtual double get_normalization_constant() const = 0;
double normalization_constant;
};
NormalizedVector
然而,这是可以理解的,因为构造函数试图调用一个纯虚函数(派生的虚表在基初始化期间不可用),因此无法编译是可以理解的。
选项 1:
派生类必须normalization_constant = get_normalization_constant();
在其构造函数中手动调用该函数。
选项 2:
对象定义一个用于初始化常量的虚函数:
init_normalization_constant() {
normalization_constant = get_normalization_constant();
}
然后由工厂构造对象:
struct NormalizedVector : public std::vector<double> {
NormalizedVector(std::initializer_list<double> init_list):
std::vector<double>(init_list) {
// init_normalization_constant();
}
double get_value(int i) const {
return (*this)[i] / normalization_constant;
}
virtual double get_normalization_constant() const = 0;
virtual void init_normalization_constant() {
normalization_constant = get_normalization_constant();
}
double normalization_constant;
};
// ...
// same code for derived types here
// ...
template <typename TYPE>
struct Factory {
template <typename ...ARGTYPES>
static TYPE construct_and_init(ARGTYPES...args) {
TYPE result(args...);
result.init_normalization_constant();
return result;
}
};
int main() {
L1Vector vec = Factory<L1Vector>::construct_and_init<std::initializer_list<double> >({0.25, 0.5, 1.0});
std::cout << "L1 ";
for (int k=0; k<vec.size(); ++k)
std::cout << vec.get_value(k) << " ";
std::cout << std::endl;
return 0;
}
选项3:
使用实际缓存:get_normalization_constant
定义为新类型,CacheFunctor;第一次CacheFunctor
被调用,它保存返回值。
在 Python 中,这与最初的编码一样工作,因为虚拟表始终存在,即使在__init__
基类中也是如此。在 C++ 中,这要复杂得多。
我非常感谢您的帮助;这对我来说很重要。我觉得我已经掌握了 C++ 中良好的面向对象设计的窍门,但在编写非常高效的代码时并不总是如此(尤其是在这种简单缓存的情况下)。