当仅函数 class 需要至少初始化一次时,C++ 替代单例设计?

C++ alternative to singleton design when a function-only class needs to be initialize at least once?

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <random>
#include <map>
#include <math.h>
#include <cstring>
using namespace std;

class MathClass {
private:
    size_t current_capacity;
    double* logfact;
    bool inited = false;
    
    MathClass() {
        current_capacity = 0;
        logfact = new double[1];
        logfact[0] = 0;
    }
    
    void calculateLogFact(int n) {
        if (current_capacity >= n) return;
        double* newLogfact = new double[n+1];
        for (int i=0; i<=current_capacity; i++) newLogfact[i] = logfact[i];
        for (int i=current_capacity+1; i<=n; i++) newLogfact[i] = newLogfact[i-1] + log(double(i));        
        delete[] logfact;
        logfact = newLogfact;
    }
    
    double factorial(int n) {
        cout << "n = " << n << "\n";
        calculateLogFact(n);
        for (int i=0; i<=n; i++) cout << int64_t(round(exp(logfact[i]))) << " ";
        cout << "\n";
        return exp(logfact[n]);
    }
    
public:

    static double factorial2n(int n) {
        static MathClass singleton;
        return singleton.factorial(2*n);
    }
};

int main(int argc, char** argv)
{    
    cout << MathClass::factorial2n(10) << "\n";
    return 0;
}

我的库需要使用一个昂贵的函数,在使用前需要初始化一次(预先计算一些昂贵的值,这样我们就不必每次都计算它们)。目前,我使用上面的单例方法。

但是,有两个问题:

  1. 多线程:如果 2 个不同的线程调用此函数,这将导致竞争条件。
  2. 人们不喜欢单身人士
  3. 其他我不知道的问题

我可以使用什么其他设计来解决这个问题?必须预先计算值,因为此功能需要快速。

我同意评论:为什么要隐藏 MathClass 缓存用户结果的事实?作为潜在用户,我看不到真正的好处,而是潜在的困惑。如果我想重用存储在实例中的先前缓存的结果,我可以这样做。你不需要将整个 class 包装在一个单例中来启用它。当您可以使用 std::vector.

时,也无需手动管理动态数组

简而言之:使用单例的替代方法是不使用单例。

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <random>
#include <map>
#include <math.h>
#include <cstring>
using namespace std;

class MathClass {
private:
    size_t current_capacity;
    std::vector<double> logfact;
    bool inited = false;
    
    void calculateLogFact(int n) {
        if (logfact.size() >= n) return;
        auto old_size= logfact.size();
        logfact.resize(n);
        for (int i=old_size; i<n; i++) logfact.push_back(logfact.back() + log(double(i)));        
    }
    
    double factorial(int n) {
        cout << "n = " << n << "\n";
        calculateLogFact(n);
        for (int i=0; i<=n; i++) cout << int64_t(round(exp(logfact[i]))) << " ";
        cout << "\n";
        return exp(logfact[n]);
    }
    
public:
    MathClass() {
        logfact.push_back(0);
    }
    double factorial2n(int n) {
       return factorial(2*n);
    }
};

void foo(MathClass& mc) { // some function using previously calculated results
    std::cout << mc.factorial2n(2);
}

int main(int argc, char** argv)
{    
    MathClass mc;
    cout << mc.factorial2n(10) << "\n";
    foo(mc);
}

我不确定数学是否正确,我懒得去检查。另外 inited 并且大多数包含似乎都未使用。

关于“Multi-threading:如果 2 个不同的线程调用此函数,这将导致竞争条件。”我也不会费心将 thread-safety 放入类型本身。想用的时候single-threaded不需要thread-safety,也不想花钱。当我想使用它时 multi-threaded,我可以通过使用我自己的 std::mutex 来保护对 mc 实例的访问。

PS:坦白说,我认为整个问题都是一个误解造成的。您的 MathClass 不是“仅功能”class。它是一个具有状态和成员函数的 class,就像任何其他 class 一样。 “误解”是向用户隐藏状态,并在实际上有状态时假装没有状态。使用此 class 时,我想控制我可以查询哪些结果,因为它们已经被缓存并且需要首先计算哪些结果。换句话说,我会提供更多访问 class 状态的权限,而不是更少。