C++经过这么多年的发展,已然成了一种文化和艺术,而这种艺术和文化并不是C++所固有的,是C++在各个方面的应用的总结和艺术化的结果。C++看起来比较复杂,但是深入其中你会发现C++是那么优美而富有哲学感。为了使C++更艺术化,C++语言大师们都为此而付出甚多,他们都在追求简单,追求编程的艺术。 
几乎所有的编程语言都有这样或那样的初始化方法,比较新的语言如C#就将很多的初始化技术都集成到编译器里面去了,而很多编程语言都需要程序员用特定的方法去实现。C++的初始化技术很大程度上就依赖于程序员。本文介绍了几种常见的、实用的初始化技术。 
1. 编译期初始化
所谓的编译期初始化,指的是符号的值在编译期就可以确定下来,一般包括普通常量的初始化和已经利用模板技术构造常量的初始化技术。 
1.1. 常量
如const int Scale = 1; 
enum ColorSpace{RGBColorSpace,CMYKColorSpace}; 
class Example{ 
static const int Count = 1; 

1.2. 利用模板
这是模板元编程实用到的一个最基本的技术。这种技术利用编译器对模板进行递归编译的机理来实现某个常量的初始化。如: 
template factorial; 
template< > factorial<1>{enum {result=1};}; 
template< > factorial{enum {result=N* factorial ::result};}; 
2. 静态初始化
所谓的静态初始化,指的是程序启动的时候初始化全局(静态)变量或静态成员编程的行为。 
2.1. 全局变量初始化
如int g_globalCount = 1; 
static int g_s_globalCount = 1; 
2.2. 静态成员变量的初始化
如class Example{ 
static Example NullExample; 

Example Example::NullExample; 
3. 动态初始化
这种初始化技术利用了类的构造和析构函数的基本特性。 
如我们需要在程序启动的时候注册某个类的创建函数到类工厂,我们可以按照下面的方式进行做: 
/////////////////////////////////////定义初始化所需要的宏等 
typedef Geometry* (*CreateFuncPtr)(); 
struct auto_register{ 
auto_register(CreateFuncPtr func){//注册} 
~auto_register(){//取消注册} 
}; 
#define AUTO_REGISTER(class_name)\ 
static const auto_register _register## class_name ##( class_name::CreateGeometry); 
//////////////////////////////////定义基于上面的初始化技术的实现 
class Geometry{ 
public: static Geometry* CreateGeometry(){…} 

class DiamondGeometry{ 
public: static DiamondGeometry* CreateGeometry(){…} 

AUTO_REGISTER(DiamondGeometry) 
class RectangleGeometry{ 
public: static RectangleGeometry* CreateGeometry(){…} 

AUTO_REGISTER(RectangleGeometry)

来自实际项目的一段代码,简化形式如下:
 switch (t)
 {
 case 0:
  int a = 0;
  break;
 default:
  break;
 }
    有什么问题吗?似乎没有。请用编译器编译一下……
    嗯?!一个错误“error C2361: initialization of 'a' is skipped by 'default' label”。这怎么可能?
    几番思琢,悟出解释:C++约定,在块语句中,对象的作用域从对象的声明语句开始直到块语句的结束,也就是说default标号后的语句是可以使用对象a的。如果程序执行时从switch处跳到default处,就会导致对象a没有被正确地初始化。确保对象的初始化可是C++的重要设计哲学,所以编译器会很严格地检查这种违例情况,像上述的示例代码中default语句后面并没有使用a,但考虑到以后代码的改动可能无意中使用,所以一样被封杀。
    明白了原因,解决起来就很容易了。只要明确地限制对象a的作用域就行了。
 switch (t)
 {
 case 0:
  {  //added for fix problem
  int a = 0;
  break;
  }  //added for fix problem
 default:
  break;
 }
    如果确实需要在整个switch语句中使用对象a,那就把int a = 0;移到switch语句之前即可。不过从原先的语句看,其意图似乎并不是这样的,所以推荐前面的解决方案。

结束了吗?没有。让我们继续考究错误提示信息中“initialization”(也就是初始化)的确切含义。C++很看重初始化,所以往往会给我们造成一种错觉,似乎对象在定义处一定会经过初始化过程。真实情况如何呢?还是用实例来证明吧。
 switch (t)
 {
 case 0:
  int a;
  a = 0;
  break;
 default:
  break;
 }
    编译,这次没有报错。很明显int a;定义了对象,但没有进行初始化,否则就应该报告原先的错误。
    再看看用户自定义类型。
 class B
 {
 };
 switch (t)
 {
 case 0:
  B b;
  break;
 default:
  break;
 }
    编译结果也没有错误,所以没有提供构造器的类仍然没有初始化过程。
    如果给类加入构造器,情况就不同了。
 class B
 {
 public:  //added for initialization
  B(){} //added for initialization
 };
    这样就能重现原先的错误。证明有了构造器,编译器就将进行初始化处理并对之进行安全检查。

从上面的实验,可以直观地体验到一些基本的C++观念和原理,并提高认识深度。
    1.int a = 0;既是声明也是定义,还包括初始化;int a;是声明还是定义依上下文而定,但如果是定义就不会包括初始化;a = 0;仅仅是赋值语句,在此句前对象已经存在了。
    2.为了避免不必要的开销,默认情况下,即程序员没有在代码中明确指示时,编译器不提供初始化过程。某些需要确保初始化的类,请提供构造器。这里透露出一个C++的设计哲学:通常你会面对多种选择,所以请精确地控制代码,其收益则是可以自由取舍调配的安全性、速度、内存开销等程序特性。
    3.严密注意程序中标号的使用情况,特别是case、default等常规标号,否则他们可能会破坏对象的正确状态。如果提供了对象初始化,则能够获得编译器的额外帮助。

深入理解C++中的初始化的更多相关文章

  1. 深入理解JDK中的I/O

    深入理解JDK中的I/O 目 录 java内存模型GCHTTP协议事务隔离级并发多线程设计模式清楚redis.memcache并且知道区别mysql分表分库有接口幂等性了解jdk8稍微了解一下特性 j ...

  2. 简单理解Struts2中拦截器与过滤器的区别及执行顺序

    简单理解Struts2中拦截器与过滤器的区别及执行顺序 当接收到一个httprequest , a) 当外部的httpservletrequest到来时 b) 初始到了servlet容器 传递给一个标 ...

  3. 深入理解JavaScript中创建对象模式的演变(原型)

    深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...

  4. 【干货理解】理解javascript中实现MVC的原理

    理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...

  5. 【转】你真的理解Python中MRO算法吗?

    你真的理解Python中MRO算法吗? MRO(Method Resolution Order):方法解析顺序. Python语言包含了很多优秀的特性,其中多重继承就是其中之一,但是多重继承会引发很多 ...

  6. 理解javascript中的策略模式

    理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...

  7. 深刻理解Java中final的作用(一):从final的作用剖析String被设计成不可变类的深层原因

    声明:本博客为原创博客,未经同意,不得转载!小伙伴们假设是在别的地方看到的话,建议还是来csdn上看吧(原文链接为http://blog.csdn.net/bettarwang/article/det ...

  8. 深入理解C++中的explicitkeyword

    深入理解C++中的explicitkeyword kezunhai@gmail.com http://blog.csdn.net/kezunhai C++中的explicitkeyword仅仅能用于修 ...

  9. 【转】Android菜单详解——理解android中的Menu--不错

    原文网址:http://www.cnblogs.com/qingblog/archive/2012/06/08/2541709.html 前言 今天看了pro android 3中menu这一章,对A ...

随机推荐

  1. WebDriver+TestNG的一个典型例子

    想让测试更加灵活,1. 可以配置使用任意支持的浏览器进行测试:2. 配置所有Google的URL:3. 配置搜索的关键字.修改后的代码: public class GoogleTest { WebDr ...

  2. 如何为 Go 设计一个通用的日志包

    需求 一个通用的日志包,应该满足以下几个需求: 兼容 log.Logger,标准库大量使用了 log.Logger 作为其错误内容的输出通道,比如 net/http.Server.ErrorLog,所 ...

  3. 使用Thumbnailator处理gif图片时遇到java.lang.ArrayIndexOutOfBoundsException: 4096异常处理

    环境 1.7.0_80 在使用Thumbnailator处理gif图片时,遇到问题: Exception in thread "main" java.lang.ArrayIndex ...

  4. Linux下Tomcat8.0.44配置使用Apr

    听说Apr可以提高tomcat很多的性能,配置具体如下 1.安装apr 1.5.2 [root@ecs-3c46 ]# cd /usr/local/src [root@ecs-3c46 src]# w ...

  5. 使用Vue搭建多页面应用

    使用Vue-cli搭建多页面应用时对项目结构和配置的调整   前提:在这里使用的是webpack模板进行搭建 第一步.安装Vue-cli并且进行初始化 首先打开git,在里面使用npm全局安装Vue- ...

  6. [译]用R语言做挖掘数据《三》

    决策树和随机森林 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到 ...

  7. Android Studio 连接 逍遥模拟器

    1.启动 逍遥模拟器: 2.打开 Android Studio 项目: 3.命令行 adb connect 127.0.0.1:21503 C:\Users\Administrator>adb ...

  8. PHP项目学习2

    通过<PHP项目学习1>基本上可以了解项目的大致结构.内容,现在直接从代码入手,开始coding吧. 现在部署环境中建立一个myonline的文件夹,便于放置我们的项目

  9. 打印插件--PAZU

    PAZU对打印控制实现了: 2.1 设置页眉页脚 2.2 指定纸张大小 2.3 设置纸张方向 2.4 设置页边距 2.5 选择指定的打印机 2.6 无需用户确认,JS直接调用打印预览 2.7 无需用户 ...

  10. Vue 引入第三方js.css的方式

    转自:https://blog.csdn.net/csdn_yudong/article/details/78795743 我们以 jQuery 为例,来讲解 一.绝对路径直接引入,全局可用 主入口页 ...