gpt4 book ai didi

c++ - 虚拟表创建线程安全吗?

转载 作者:行者123 更新时间:2023-12-01 11:55:52 25 4
gpt4 key购买 nike

请让我开始,我知道从构造函数/析构函数中调用虚函数是一种不好的做法。
然而,这样做的行为,尽管它可能令人困惑或不是用户所期望的,但仍然是明确定义的。

struct Base
{
Base()
{
Foo();
}
virtual ~Base() = default;
virtual void Foo() const
{
std::cout << "Base" << std::endl;
}
};

struct Derived : public Base
{
virtual void Foo() const
{
std::cout << "Derived" << std::endl;
}
};

int main(int argc, char** argv)
{
Base base;
Derived derived;
return 0;
}

Output:
Base
Base

现在,回到我真正的问题。如果用户从不同线程的构造函数中调用虚函数会发生什么。有竞争条件吗?它是未定义的吗?
或者换句话说。编译器设置 vtable 是线程安全的吗?

例子:
struct Base
{
Base() :
future_(std::async(std::launch::async, [this] { Foo(); }))
{
}
virtual ~Base() = default;

virtual void Foo() const
{
std::cout << "Base" << std::endl;
}

std::future<void> future_;
};

struct Derived : public Base
{
virtual void Foo() const
{
std::cout << "Derived" << std::endl;
}
};

int main(int argc, char** argv)
{
Base base;
Derived derived;
return 0;
}

Output:
?

最佳答案

首先是与此上下文相关的标准的一些摘录:

[defns.dynamic.type]

type of the most derived object to which the glvalue refers [Example: If a pointer p whose static type is "pointer to class B" is pointing to an object of class D, derived from B, the dynamic type of the expression *p is "D". References are treated similarly. — end example]



[intro.object] 6.7.2.1

[..] An object has a type. Some objects are polymorphic; the implementation generates information associated with each such object that makes it possible to determine that object's type during program execution.



[class.cdtor] 11.10.4.4

Member functions, including virtual functions, can be called during construction or destruction. When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x ) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class. [..]



正如您所写的,它清楚地定义了构造函数/析构函数中虚函数调用的工作方式——它们取决于对象的动态类型,以及与对象关联的动态类型信息,这些信息在执行过程中会发生变化。您使用哪种指针来“查看对象”无关紧要。考虑这个例子:
struct Base {
Base() {
print_type(this);
}

virtual ~Base() = default;

static void print_type(Base* obj) {
std::cout << "obj has type: " << typeid(*obj).name() << std::endl;
}
};

struct Derived : public Base {
Derived() {
print_type(this);
}
};
print_type总是收到指向 Base 的指针,但是当您创建 Derived 的实例时您将看到两行 - 一行带有“Base”,另一行带有“Derived”。动态类型在构造函数的最开始设置,因此您可以调用虚函数作为成员初始化的一部分。

未指定此信息的存储方式或位置,但它与对象本身相关联。

[..] the implementation generates information associated with each such object [..]



为了更改动态类型,必须更新此信息。这可能是编译器引入的一些数据,但对这些数据的操作仍然被内存模型覆盖:

[intro.memory] 6.7.1.3

A memory location is either an object of scalar type or a maximal sequence of adjacent bit-fields all having nonzero width. [ Note: Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation. — end note]



因此与对象相关的信息被存储和更新在某个内存位置。但这就是数据竞争的发生:

[intro.races]

[..]
Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location.
[..]
The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other [..]



动态类型的更新不是原子的,并且由于没有其他同步可以强制执行先发生顺序,因此这是数据竞争,因此是 UB。

即使更新是原子的,只要构造函数尚未完成,您仍然无法保证对象的状态,因此没有必要将其设为原子。

更新

从概念上讲,感觉对象在构造和销毁过程中呈现出不同的类型。但是,@LanguageLawyer 已经向我指出,对象的动态类型(更准确地说是引用该对象的泛左值)对应于最派生的类型,并且该类型定义明确且不会更改。 [class.cdtor]还包括有关此细节的提示:

[..] the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.



因此,即使虚拟函数调用和 typeid 运算符的行为被定义为好像对象采用不同的类型,但实际上并非如此。

也就是说,为了实现指定的行为,必须更改对象状态中的某些内容(或至少与该对象相关联的某些信息)。正如 [intro.memory] 所指出的,这些额外的内存位置确实是内存模型的主题。所以我仍然支持我最初的评估,即这是一场数据竞赛。

关于c++ - 虚拟表创建线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62288195/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com