2つの派生クラスで共有される計算をキャッシュする方法についてアドバイスをお願いします。例として、2種類の正規化ベクトル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()
このコードは、構築後に変更されない場合でも、繰り返し呼び出されるため、大きなベクトルの場合は不必要に遅くなります(などの修飾子push_back
が適切に無効になっていると仮定します)。
正規化の1つの形式のみを検討している場合は、単純にdouble値を使用して、構築時にこの結果をキャッシュします。
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
呼び出されると、戻り値が保存されます。
__init__
Pythonでは、基本クラスであっても仮想テーブルが常に存在するため、これは元のコードどおりに機能します。C ++では、これははるかに注意が必要です。
私は本当に助けていただければ幸いです。これは私にとって多くのことです。私はC++で優れたオブジェクト指向設計のコツをつかんでいるように感じますが、非常に効率的なコードを作成する場合は必ずしもそうとは限りません(特に、この種の単純なキャッシュの場合)。