用户定义的类和标准类之间有区别吗?

Is there a difference between user defined classes and std classes?

本文关键字:有区别 之间 定义 用户 标准      更新时间:2023-10-16

据我所知,构造函数/析构函数调用方式的唯一区别取决于实例化顺序。但是,"普通"用户类和 std 中的类之间还有其他区别吗?

假设我有一个名为myStackOverflow的类,然后我实例化了这个类的一个对象。我在同一范围内还有一个类型std::thread的对象。这些对象的构造函数或析构函数的调用方式是否有任何差异?

我问这个问题的主要原因是,根据C++标准(第 30.3.2.3 节),join()不会在线程析构函数中调用。您可以在此处阅读原因。但是在这个关于堆栈溢出的答案中,提到将std::thread包装在另一个类中,然后在这个新包装类的析构函数中调用join()。我不明白这将如何解决任何问题。析构函数仍然是析构函数,在std::thread的析构函数中调用join()的危险仍然存在。

唯一有意义的方法是,这两种不同类型的析构函数的调用方式之间存在差异。

好的@Ali,让我们简要概述一些事情(术语)。

namespace std- 是一个简单的命名空间,例如:

namespace my_namespace {
int my_integer;
};

它包含大量对程序员有用的类,由超强的程序员编写,并且,正如你所理解的,它应该尽可能灵活(后来的"语句 1"),因为不同的人有不同的需求。

当然,它遵守 c++ 标准的一般规则,因为它的内容也是如此。


现在让我们谈谈您想要的std::thread

它是一个表示单个执行线程的简单类。它允许您在"外空间"中执行你的函数,并与我们的抽象"外空间"保持联系,它还处理一些数据:

  • _id - 线程 ID
  • _handle - 秘密变量,它是与"外太空"通信的关键
  • 当然,它会保留一些数据 关于执行状态(是否有任何实体现在在某处执行 钥匙引导我们到的"外太空")

如果您更详细地查找一些参考资料并记住我们的"声明 1",您会注意到以下信息:

  • "std::thread对象也可能处于不表示任何线程的状态">
  • "没有两个std::thread对象可以代表相同的执行线程;std::thread不是CopyConstructible或CopyAssignable,尽管它是MoveConstructible和MoveAssignable。

现在您应该得出结论,std::thread变量和执行实体是分开的,但尝试删除附加到执行实体std::thread变量会引发异常。

但是,当实体完成执行时,std::thread变量将保持活动状态,并且可以附加到任何其他实体。

对于这些需求,有以下方法:

join() // waits for a thread to finish its execution
// if it is attached to something, the code execution will not go futher until our entity finishes its execution.
detach() // permits the thread to execute independently from the thread handle 
// detaches our std:: thread variable from executing entity, now our entity lives its own life. std::thread variable may be removed while the entity keeps alive
joinable() // checks whether the thread is joinable, i.e. potentially running in parallel context
// if thread is attached to something which is executing now, it returns true, otherwise false

这里有一些代码示例可以消除您的误解:

#include <iostream>
#include <thread>
#include <chrono>
using namespace::std;
void some_func1() {
cout << "some_func1 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func1 thread finished " << endl;
}
void some_func2() {
cout << "some_func2 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func2 thread finished " << endl;
}

int main() {    
thread some_thread;
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread = thread(some_func1);
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread.detach();
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
this_thread::sleep_for(chrono::seconds(1)); 
some_thread = thread(some_func2);
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread.join();
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
}
//  Output is:
//  Is some_thread joinable: 0
//  some_func1 thread started
//  Is some_thread joinable: 1
//  Is some_thread joinable: 0
//  some_func2 thread started
//  Is some_thread joinable: 1
//  some_func1 thread finished
//  some_func2 thread finished
//  Is some_thread joinable: 0
//  Press any key to continue . . .

如果你想确保你的实体在线程变量删除之前完成执行,你可以把它包装在另一个类中,并在析构函数中调用join(),正如你提到的。

或者你也可以把它包装在另一个将在析构函数中调用detach()

这 2 种方式将防止您遭受大量暗恋。

#include <iostream>
#include <thread>
#include <chrono>
using namespace std;

typedef void Myfunc();
void some_func1() {
cout << "some_func1 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func1 thread finished " << endl;
}
void some_func2() {
cout << "some_func2 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func2 thread finished " << endl;
}
class ICareAboutThread {
std::thread thread_;
public:
ICareAboutThread( Myfunc f = nullptr) : thread_(f) {};
~ICareAboutThread() { join(); }
bool joinable() { return thread_.joinable();}
void join() { thread_.join();}
void detach() { thread_.detach();}
// other constructors : move , safe copying - if necessary;
};

class IDontCareAboutThread {
std::thread thread_;
public:
IDontCareAboutThread(Myfunc f = nullptr) : thread_(f) {};
~IDontCareAboutThread() { detach(); }
bool joinable() { return thread_.joinable(); }
void join() { thread_.join(); }
void detach() { thread_.detach(); }
// other constructors : move , safe copying - if necessary;
};
int main() {    
ICareAboutThread i_care(some_func1);
this_thread::sleep_for(chrono::seconds(1));
IDontCareAboutThread i_dont_care(some_func2);
return 0;
}
//  Output is:
//  some_func1 thread started
//  some_func2 thread started
//  some_func1 thread finished
//  Press any key to continue . . .

现在我希望它对您来说很清楚,如果您了解变量何时删除:D