c++构造函数,静态成员和多线程问题求助
最近学习c++多线程时候碰到的问题:#include <iostream>
#include<thread>
#include<mutex>
#include<Windows.h>
static std::once_flag once;
class Log
{
public:
//Log() {};//(问题一)我发现这里为啥非要加一个构造函数?不加构造函数就会提示“类 "Log" 不存在默认构造函数”
Log(const Log& log) = delete;
Log& operator=(const Log& log) = delete;
static Log& GetInstance()
{
Log* log = NULL;
std::call_once(once, init, log);
return *log;
}
static void init(Log* log)
{
if (log == NULL)log = new Log;
}
void PrintLog(std::string msg)
{
std::cout << __TIME__ << "" << msg << '\n';
}
private:
//static std::once_flag once; (问题二)为啥这个放在类外面就没事了?放在类里面就会报错“无法解析的外部符号 "private: static struct std::once_flag Log::once" (?once@Log@@0Uonce_flag@std@@A)”
};
void PrintError()
{
Log::GetInstance().PrintLog("error\n");
}
int main()
{
std::thread t1(PrintError);
std::thread t2(PrintError);
t1.join();
t2.join();
}
哈哈,这个问题看起来蛮有意思的,尤其是要在多线程的背景下处理构造函数和静态成员的问题。让我们一条一条地解决你的疑惑吧。
### 问题一:为什么需要一个构造函数?
首先,C++中的类如果不显式声明构造函数,编译器会默认生成一个默认的构造函数。然而,如果你在类里定义了其他构造函数(即使是拷贝构造函数、移动构造函数等),编译器就不会提供默认构造函数了。所以你的代码中:
//Log() {}; // 注释掉了默认构造函数
Log(const Log& log) = delete;
Log& operator=(const Log& log) = delete;
由于你手动删除了拷贝构造函数,因此编译器不会再为你生成默认构造函数。这时你就必须自己声明一个默认的构造函数(尽管它啥都不干)。
Log() {};// 这个构造函数还是要有的呀!
### 问题二:静态变量和多线程
你的 `Log` 类是个经典的单例模式实现方法,用来确保多线程情况下只有一个 `Log` 实例。根据你的代码:
static Log& GetInstance()
{
Log* log = NULL;
std::call_once(once, init, log);
return *log;
}
static void init(Log* log)
{
if (log == NULL) log = new Log;
}
这里的问题在于:`log` 是局部变量,导致即使在 `init` 函数里 `log` 变成了指向 `new Log` 的指针,也不会回传给 `GetInstance` 里面的 `log`。
但其实这个问题可以通过以下改进来解决:
static Log& GetInstance()
{
static Log* log = NULL;// 将log变成静态的
std::call_once(once, init, log);
return *log;
}
static void init(Log*& log)
{
if (log == NULL) log = new Log;
}
这样一来,`log` 变成了 `GetInstance` 函数内的静态变量,并且在 `init` 函数参数里使用引用来保证修改后的值能传递回来。
### 小幽默时刻
哎呀,C++真的很像养宠物——静态成员就像我们的猫,它们喜欢独处,所有人共享一份。而多线程就像试图同时给猫喂饭的一大群人,得小心别被抓,确保大家协调好了才行!所以,使用 `std::call_once` 就像给这群人派了个小队长,确保他们不会手忙脚乱,猫也不会炸毛啦~
希望这个解释对你有帮助!如果还有什么其他疑问,请随时提问,我会继续为你解答哒!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。 FishC 发表于 2024-7-19 13:21
哈哈,这个问题看起来蛮有意思的,尤其是要在多线程的背景下处理构造函数和静态成员的问题。让我们一条一条 ...
为什么 static std::once_flag once;放在类里面就报错"private: static struct std::once_flag Log::once" (?once@Log@@0Uonce_flag@std@@A),必须放在类外面? sun_chuan 发表于 2024-07-19 13:27
为什么 static std::once_flag once;放在类里面就 ...
哈哈,C++的细节真的是一波接一波呀!让我们揭开这个谜底吧~
### 问题解析
事实上,这是因为在C++中静态成员变量的初始化规则。静态成员变量必须在类定义外面进行初始化。这是C++语言的一个规定,因为静态成员变量在所有实例中是共享的,它们并不随每个对象的创建而重新分配存储空间,而是从属于整个类的。因此,我们要在类外对它进行定义和初始化。简单来说,你需要告诉编译器,这个静态变量是在类外部定义的。
### 解决方案
你可以这样来解决这个问题:
1. 在类内部声明静态成员变量。
2. 在类外部定义并初始化静态成员变量。
下面是改进后的例子:
#include <iostream>
#include <mutex>
class Log
{
public:
// 默认构造函数
Log() {}
// 禁用拷贝构造函数和赋值运算符
Log(const Log& log) = delete;
Log& operator=(const Log& log) = delete;
static Log& GetInstance()
{
std::call_once(once, &Log::init); // 注意这里的改变
return *log;
}
void PrintLog(std::string msg)
{
std::cout << msg << std::endl;
}
private:
static void init()
{
log = new Log;
}
static Log* log;
static std::once_flag once;
};
// 在类外部定义并初始化静态成员变量
Log* Log::log = nullptr;
std::once_flag Log::once;
int main()
{
Log& logger = Log::GetInstance();
logger.PrintLog("Hello from FishC!");
return 0;
}
### 为什么这样做?
- **声明 vs 定义**:在类内部,你只是在声明 `std::once_flag`,告诉编译器这个类里会有一个这样的静态成员变量。但是你并没有定义它,也没有为它分配内存空间。
- **类外定义**:在类外,你才真正定义了这个静态变量,并且为它分配了内存空间和初值。
### 小幽默时刻
C++ 的这些规则就像我们做菜时的“先声明后定义”原则。你得先告诉大家“今晚吃火锅”,然后再去买菜准备食材。而不能说“我已经准备好火锅了”,但其实家里连锅还没买。这就是为啥一定要在类外定义和初始化这些静态成员变量啦!
希望这次的解释清楚明了,如果还有什么头疼的C++问题,尽管问,我会继续用通俗易懂的方式为你解答哒!
以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
页:
[1]