首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >缓存单个覆盖的计算C++11

缓存单个覆盖的计算C++11
EN

Stack Overflow用户
提问于 2012-10-02 17:25:37
回答 2查看 140关注 0票数 1

我想知道如何缓存由两个派生类共享的计算。作为一个例子,我有两种类型的归一化向量L1和L2,它们各自定义了自己的规范化常量(注意:针对好的实践,我在这里继承std::vector作为一个快速说明--信不信由你,我真正的问题不是L1和L2向量!):

代码语言:javascript
运行
复制
#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这样的修饰符已经被适当禁用)。

如果我只考虑一种形式的规范化,我只需要使用一个双值来缓存这个构造结果:

代码语言:javascript
运行
复制
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:对象定义一个用于初始化常量的虚拟函数:

代码语言:javascript
运行
复制
init_normalization_constant() {
  normalization_constant = get_normalization_constant();
}

然后,由工厂构造对象:

代码语言:javascript
运行
复制
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++中良好的面向对象设计的诀窍,但在编写非常高效的代码时(特别是在这种简单缓存的情况下),我并不总是这样。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2012-10-02 17:33:03

我建议采用非虚拟接口模式。当您想要一个方法同时提供通用和唯一的功能时,这种模式就很出色了。(在这种情况下,缓存是共同的,计算是唯一的。)

Interface

代码语言:javascript
运行
复制
// UNTESTED
struct NormalizedVector : public std::vector<double> {
 ...
  double normalization_constant;
  bool cached;
  virtual double do_get_normalization_constant() = 0;
  double get_normalization_constant() {
    if(!cached) {
      cached = true;
      normalization_constant = do_get_normalization_constant();
    }
    return normalization_constant;
};

附注:您确实不应该公开地从std::vector派生。

P.P.s.使缓存无效就像将cached设置为false一样简单。

完全解

代码语言:javascript
运行
复制
#include <vector>
#include <iostream>
#include <iterator>
#include <cmath>
#include <algorithm>

struct NormalizedVector : private std::vector<double> {
private:
  typedef std::vector<double> Base;
protected:
  using Base::operator[];
  using Base::begin;
  using Base::end;
public:
  using Base::size;

  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 do_get_normalization_constant() const = 0;
  mutable bool normalization_constant_valid;
  mutable double normalization_constant;
  double get_normalization_constant() const {
    if(!normalization_constant_valid) {
      normalization_constant = do_get_normalization_constant();
      normalization_constant_valid = true;
    }
    return normalization_constant;
  }

  void push_back(const double& value) {
    normalization_constant_valid = false;
    Base::push_back(value);
  }

  virtual ~NormalizedVector() {}
};

struct L1Vector : public NormalizedVector {
  L1Vector(std::initializer_list<double> init_list):
    NormalizedVector(init_list) { get_normalization_constant(); }
  double do_get_normalization_constant() const {
    return std::accumulate(begin(), end(), 0.0);
  }
};

struct L2Vector : public NormalizedVector {
  L2Vector(std::initializer_list<double> init_list):
    NormalizedVector(init_list) { get_normalization_constant(); }
  double do_get_normalization_constant() const {
    return std::sqrt(
      std::accumulate(begin(), end(), 0.0,
        [](double a, double b) { return a + b * b; } ) );
  }
};

std::ostream&
operator<<(std::ostream& os, NormalizedVector& vec) {
  for (int k=0; k<vec.size(); ++k)
    os << vec.get_value(k) << " ";
  return os;
}

int main() {
  L1Vector vec{0.25, 0.5, 1.0};
  std::cout << "L1 " << vec << "\n";

  vec.push_back(2.0);
  std::cout << "L1 " << vec << "\n";

  L2Vector vec2{0.25, 0.5, 1.0};
  std::cout << "L2 " << vec2 << "\n";

  vec2.push_back(2.0);
  std::cout << "L2 " << vec2 << "\n";
  return 0;
}
票数 5
EN

Stack Overflow用户

发布于 2012-10-02 17:32:53

一个快速而肮脏的解决方案是使用静态成员变量。

代码语言:javascript
运行
复制
  double get_normalization_constant() const {
    static double tot = 0.0;
    if( tot == 0.0 )
    for (int k=0; k<size(); ++k)
      tot += (*this)[k];
    return tot;
  }

在这种情况下,它将只计算一次。每次它都会返回最新的值。

注意:这个双tot,将共享所有相同类型的对象。如果要创建多个L1Vector类型的对象,请不要使用它

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12695184

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档