所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象都被排除。这种对象包括global对象、定义于namespace作用域内的对象,classes内、在函数内、以及在file作用域内被声明为static的对象。函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象成为non-local static对象,在程序结束时static对象会被自动销毁,也就是他们的析构函数会在main()结束时被调用

c++对“定义于不同的编译单元内的non-local static对象”的初始化次序并无明确定义。

为了更清晰的理解上述文字,特意写了一个错误的实例,代码如下:

//a1.cpp
#include <fstream>
#include <iostream>
#include "a3.cpp"
using namespace std;
Write a;
int main()
{ system("pause");
return 0;
}
//a2.cpp
#include <fstream>
using namespace std;
extern ofstream out("a6.txt");
//a3.cpp
#include <fstream>
using namespace std;
extern ofstream out;
class Write
{
public:
Write()
{
out<<"a4.txt";
} };

上述代码,a2.cpp中,定义了全局变量 out,a3.cpp中定义了一个类 Write,它的构造函数初始化依赖于 out,因为2个文件的便宜顺序是不确定的,所以,很有可能 当Write()调用的时候,out全局变量并没有被初始化,造成程序错误。

案例2:

先上代码:

//FileSystem.h
#include <iostream>
class FileSystem
{
public:
FileSystem(int a):num(a)
{
}
std::size_t numDisks() const; private:
int num; };
//FileSystem.cpp
#include "FileSystem.h"
std::size_t FileSystem::numDisks() const
{
return num;
}
//Directory.h
#include <iostream>
#include "FileSystem.h"
class Directory
{
public:
Directory();
int display();
private:
int a;
};
//Directory.cpp
#include "Directory.h"
extern FileSystem tfs;
Directory::Directory()
{
std::size_t disks = tfs.numDisks();
a = disks;
}
int Directory::display()
{
return a;
}
Directory a;
//Y.cpp
#include "FileSystem.h"
extern FileSystem tfs(10);
//X.cpp
#include "Directory.h"
#include <iostream>
using namespace std;
extern Directory a;
int main()
{ cout<<a.display()<<endl;
system("pause");
}

代码中 Directory对象的初始化依赖于 全局对象FileSystem tfs,因为C++“对定义于不同的编译单元内的non-local static对象”的初始化相对次序并没有明确定义,所以很有可能在 Y.cpp里的变量tfs初始化之前,先初始化 Directory.cpp里的对象a,则a就得不到我们想要的值。

因为C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇到该对象的定义式”时被初始化,所以代码修改如下:

//FileSystem.h
#include <iostream>
class FileSystem
{
public:
FileSystem(int a):num(a)
{
}
std::size_t numDisks() const; private:
int num; };
//FileSystem.cpp
#include "FileSystem.h"
std::size_t FileSystem::numDisks() const
{
return num;
} //Directory.h
#include <iostream>
#include "FileSystem.h"
class Directory
{
public:
Directory();
int display();
private:
int a;
};
//Directory.cpp
#include "Directory.h"
extern FileSystem& tfs();
Directory::Directory()
{
std::size_t disks = tfs().numDisks(); //tfs()函数返回一个FileSystem& 对象
a = disks;
}
int Directory::display()
{
return a;
}
Directory& tempDir()
{
static Directory td;
return td;
}
//Y.cpp
#include "FileSystem.h"
FileSystem& tfs()
{
static FileSystem fs(10); //在一个函数里,返回静态对象
return fs;
}
//X.cpp
#include "Directory.h"
#include <iostream>
using namespace std;
extern Directory& tempDir();
Directory a=tempDir();
int main()
{ cout<<a.display()<<endl;
system("pause");
}

non-local static 变量初始化顺序不确定,带来的问题的更多相关文章

  1. 调整static变量初始化顺序的一个办法

    // wrap the LaunchDir variable in a function to work around static/global initialization order stati ...

  2. 【细说Java】Java变量初始化顺序

    Java的变量初始化顺序,对这里一直似懂非懂,面试的时候也经常被问到,但答的一直不好,现在整理记录一下,以后忘记了可以来看看. 程序分为两个部分,第一个部分不考虑继承,第二个部分考虑继承: (1)不考 ...

  3. c++ 类与函数中static变量初始化问题(转)

    首先static变量只有一次初始化,不管在类中还是在函数中..有这样一个函数: void Foo() { ; // initialize std::cout << a; a++; } 里的 ...

  4. C++成员变量初始化顺序问题

    由于面试题中,考官出了一道简单的程序输出结果值的题:如下, class A { private: int n1; int n2; public: A():n2(0),n1(n2+2){} void P ...

  5. java变量初始化顺序

    第一次实例化一个类时,初始化优先顺序为: 1.父类中的静态成员变量和静态代码块初始化 2.本类中的静态成员变量和静态代码块初始化 3.父类中的实例成员初始化 4.父类中的构造方法 5.本类中的实例成员 ...

  6. Java静态方法,静态变量,初始化顺序

    1. 静态方法: 成员变量分为实例变量和静态变量.其中实例变量属于某一个具体的实例,必须在类实例化后才真正存在,不同的对象拥有不同的实例变量.而静态变量被该类所有的对象公有(相当于全局变量),不需要实 ...

  7. Java中类成员变量初始化顺序

    一. 定义处默认初始化vs构造函数中初始化 java中类成员变量支持在声明处初始化,也可以在构造函数中初始化,那么这两者有什么区别呢?看下面例子 public class FieldsInit { p ...

  8. java静态类、静态方法、静态代码块,静态变量及实例方法,实例变量初始化顺序及内存管理,机制

    1.当一个类被第一次使用时,它需要被类加载器加载,而加载过程涉及以下两点: (1)在加载一个类时,如果它的父类还未被加载,那么其父类必须先被加载: (2)当类加载到内存之后,按照在代码中的出现顺序执行 ...

  9. Java类的变量初始化顺序

    大家在去参加面试的时候,经常会遇到这样的考题:给你两个类的代码,它们之间是继承的关系,每个类里只有构造器方法和一些变量,构造器里可能还有一段代码对变量值进行了某种运算,另外还有一些将变量值输出到控制台 ...

随机推荐

  1. 1064 朋友数 (20 分)C语言

    如果两个整数各位数字的和是一样的,则被称为是"朋友数",而那个公共的和就是它们的"朋友证号".例如 123 和 51 就是朋友数,因为 1+2+3 = 5+1 ...

  2. 01_垂直居中body中的应用

    1: 应用场景 在body中书写一个代码块, 使其相对于body垂直居中 <!DOCTYPE html> <html lang="en"> <head ...

  3. 彻底掌握CORS跨源资源共享

    本文来自于公众号链接: 彻底掌握CORS跨源资源共享 ) 本文接上篇公众号文章:彻底理解浏览器同源策略SOP 一.概述 在云时代,各种SAAS应用层出不穷,各种互联网API接口越来越丰富,H5技术在微 ...

  4. 【Spark 内核】 Spark 内核解析-下

    Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制.Spark任务调度机制.Spark内存管理机制.Spark核心功能的运行原理等,熟练掌握Spark内核原理,能够帮助我们更 ...

  5. ODBC连接时报错不可识别的数据库格式

    报这个错误是因为Acess的版本不同. 2003版本的Acess的数据连接字符串: string dataBasePath = @"C:/Users/user/Documents/Test. ...

  6. STM321的SPI驱动遇到的一个坑!!

    最近在做项目要用到FATFS文件驱动和SD卡驱动,SD卡驱动我用的是SPI的通信方式,在挂载文件系统是总是挂在失败了,花了一天时间反复检查,才发现SPI在接收时候卡死: 为了寻找问题的原因,整个人都快 ...

  7. VMware Workstation CentOS7 Linux 学习之路(3)--.net coreWeb部署

    1.首先创建一个文件夹,命名为core mkdir core cd core 2.我这里用FlashFXP连接Linux 把我发布的项目上传到CentOS7的core文件夹下 此时我输入命令 dotn ...

  8. 轻松弄懂var、let、const之间的区别

    ECMAScript 6(简称ES6)是JavaScript语言的下一代标准,于2015年6月正式发布,也称ECMAScript 2015. ES6的好处 ES6的出现为我们前端带来了很多方便之处,以 ...

  9. Docker 学习 1 入门

    Docker 学习 1 入门 dockert 安装. Mac Ubuntu 查看docker 版本 docker version 拉取image. docker pull e.g docker pul ...

  10. springboot整合@Scheduled定时任务的使用

    1.启动类里面添加注解@EnableScheduling ,例如: @SpringBootApplication@EnableScheduling@MapperScan("com.examp ...