当仅函数 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;
}
我的库需要使用一个昂贵的函数,在使用前需要初始化一次(预先计算一些昂贵的值,这样我们就不必每次都计算它们)。目前,我使用上面的单例方法。
但是,有两个问题:
- 多线程:如果 2 个不同的线程调用此函数,这将导致竞争条件。
- 人们不喜欢单身人士
- 其他我不知道的问题
我可以使用什么其他设计来解决这个问题?必须预先计算值,因为此功能需要快速。
我同意评论:为什么要隐藏 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 状态的权限,而不是更少。
#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;
}
我的库需要使用一个昂贵的函数,在使用前需要初始化一次(预先计算一些昂贵的值,这样我们就不必每次都计算它们)。目前,我使用上面的单例方法。
但是,有两个问题:
- 多线程:如果 2 个不同的线程调用此函数,这将导致竞争条件。
- 人们不喜欢单身人士
- 其他我不知道的问题
我可以使用什么其他设计来解决这个问题?必须预先计算值,因为此功能需要快速。
我同意评论:为什么要隐藏 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 状态的权限,而不是更少。