类heirarchy

时间:2015-10-17 23:21:33

标签: c++ initialization static-initialization initialization-order

我最近痛苦地意识到Static Initialization Order Fiasco。我想知道,如果规则"初始化顺序未在翻译单元中定义"仍然适用于父类中的静态成员,子类中的静态成员需要这些成员。

例如,假设我们(为简洁起见,除了所有#guard和include)

// a.h
class A {
    static int count;
    static int register_subclass();
};

// a.cpp
int A::count = 0;
int A::register_subclass() {
    return count ++;
}

然后是A

的子类
// b.h
class B : public A {
    static int id;
};

// b.cpp
int B::id = A::register_subclass();

这里有两个转换单元,其中一个静态对象在初始化中依赖于另一个静态对象...它似乎可能是静态初始化顺序fiasco的一个实例。

我的问题是:实际上是否安全?

也就是说,我保证B::idA::count初始化之前不会包含从A复制的垃圾?根据我自己的测试, imagesForVideo = [] imagesForVideoCGI = [] var timesArray:[NSValue] = [] let generator:AVAssetImageGenerator = AVAssetImageGenerator(asset: sourceAsset) for var i = 0; i < numberOfFrames - 1; i++ { var actualTime : CMTime = CMTimeMake(0, 0) let duration:CMTime = CMTimeMake(Int64(i), Int32(30)) let frameRef:CGImageRef = try! generator.copyCGImageAtTime(duration, actualTime: &actualTime) let tempImage:UIImage = UIImage(CGImage: frameRef) let nsDuration = NSValue.init(CMTime: duration) timesArray.append(nsDuration) imagesForVideoCGI.append(frameRef) imagesForVideo.append(tempImage) } generator.generateCGImagesAsynchronouslyForTimes(timesArray, completionHandler: {(_, im:CGImage?, _, _, e:NSError?) in self.addingImages(im)}) 似乎总是先被初始化,但我不确定如何在初始化顺序中引入噪声,以增加失败的可能性,如果行为未定义。

2 个答案:

答案 0 :(得分:3)

通常,依赖基类和派生类的静态初始化顺序是不安全的。无法保证... if (req.method.toLowerCase() === 'get') { var fileUrl = 'https://yeketakclub.storage.googleapis.com/images/yeketak.club-4rjngDhckqZ9ZBmG2fsv.jpeg'; var filename = fileUrl.split('/').pop(); res.setHeader('Content-disposition', 'attachment; filename=' + filename); request(fileUrl).pipe(res); } else { res.writeHead(404); res.end('Nein Nein :))'); } ... 的静态初始化将在A之前发生。这是static initialization order fiasco的定义。

您可以在首次使用时使用构造 idiom:

B

Live demo.

更新:然而,说,博格丹在评论中指出

  

根据标准中的[3.6.2],保证了该特定示例中的初始化顺序。它与继承无关,但事实上// a.h class A { private: static int& count(); protected: static int register_subclass(); }; // a.cpp int& A::count() { static int count = 0; return count; } int A::register_subclass() { return count()++; } // b.h class B : public A { public: static int id; }; // b.cpp int B::id = A::register_subclass(); 的初始化是常量初始化,这保证在动态初始化之前完成,是A::count使用的。

但是,除非你完全掌握这些内容,否则我建议你在第一次使用习语时使用构造。

在这种情况下可以,但在多线程上下文中要注意B::id之类的函数。如果多个线程同时调用它可能会发生任何事情

答案 1 :(得分:0)

  

我想知道,如果“翻译单元未定义初始化顺序”的规则仍然适用于父类中静态成员,而子类中的静态成员需要这些规则。

是的,确实如此。

静态数据成员与继承层次结构(或者,实际上,它们的封装类)的唯一方式是它们的完全限定名称;他们的定义和初始化完全没有意识到/不关心这个。