在构造函数中引用此指针

Referencing this pointer in constructor

Following is another example of forward declaration, which might be useful if the application needs a self-sustaining array of objects which is able to add and remove objects from itself during run-time:

File a.h:

class A {
public:
    static A *first, *last;
    A *previous, *next;

    A();
    ~A();
};

File a.cpp:

#include "a.h"

A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error

A::A() {
    if(first==0) first=this; //first A created
    previous=last;
    if(previous != 0) previous->next=this;
    last=this;
    next=0;
}

A::~A() {
    if(previous != 0) previous->next=next;
    if(next != 0) next->previous=previous;
}

发件人:https://en.wikipedia.org/wiki/Circular_dependency#Self-reference_example

我认为 A 的实施存在问题。在构造 A 的第一个实例时,如果其他线程引用 A::first,则会导致意外行为。如果我错了请更正。

另外,请问如何解决这个问题?谢谢。

您需要将 firstlast 变量声明为原子变量,例如使用 std::atomic
或者用 mutex

保护它们

是的,没错。他们需要受到保护。此外,previousnext 也需要保护。例如

A::~A() {
    unique_lock<std::mutex> locked(A::A_mutex);
    if(previous != 0) previous->next=next;
    if(next != 0) next->previous=previous;
}

其中 A_mutex 是静态成员。

正如其他人在评论中所述,此代码绝对是非线程安全的。无论是否是第一个对象,如果有 2 个线程同时尝试创建或删除 A 对象,您会得到未定义的行为,因为两个不同的线程在没有任何同步的情况下使用和更改相同的静态值。

可以做什么?一如既往地有两个相同的选项:

  • 记录 class(至少构造函数和析构函数)不是线程安全的。由于它们,调用者有责任确保只有一个线程可以同时访问 A 对象。换句话说,这意味着A只有在单线程程序中才会安全。
  • 在 class 自身内部添加同步以使其线程安全。当创建和销毁操作静态成员时,您需要 class 的所有对象的全局互斥锁,换句话说 class 静态对象。

正如@zvone 在评论中注意到的那样,当您删除链中的第一个或最后一个元素时,析构函数可以使 firstlast 成为悬挂指针。

析构函数(非线程安全版本)应该是:

A::~A() {
    if(previous != 0) previous->next=next;
    if(next != 0) next->previous=previous;
    if (first == this) first = next;
    if (last == this) last = previous;
}

线程安全版本可以是:

文件a.h

class A {
public:
    static A *first, *last;
    A *previous, *next;
    static std::mutex mut;

    A();
    ~A();
};

文件a.cpp:

#include "a.h"

A *A::first=0, *A::last=0; // don't put the word static here, that will cause an error
std::mutex A::mut;

A::A() {
    mut.lock()
    if(first==0) first=this; //first A created
    previous=last;
    if(previous != 0) previous->next=this;
    last=this;
    next=0;
    mut.unlock();
}

A::~A() {
    mut.lock()
    if(previous != 0) previous->next=next;
    if(next != 0) next->previous=previous;
    if (first == this) first = next;
    if (last == this) last = previous;
    mut.unlock();
}

这里答案的问题是他们没有指出 previous 和 next 是 public 成员的事实 - 这意味着,构造函数和析构函数中的互斥锁没有保护可以使 class 完全线程安全。使这个 class 线程安全的唯一方法是隐藏这些成员并提供访问它们的方法 - 每个内部都有一个同步原语。