zoukankan      html  css  js  c++  java
  • (转)C++初始化与赋值

    来源:http://www.cnblogs.com/chio/archive/2008/10/06/1305145.html

    先来个区别说明:赋值操作是在两个已经存在的对象间进行的,而初始化是要创建一个新的对象,并且其初值来源于另一个已存在的对象。编译器会区别这两种情 况,赋值的时候调用重载的赋值运算符,初始化的时候调用拷贝构造函数。如果类中没有拷贝构造函数,则编译器会提供一个默认的。这个默认的拷贝构造函数只是 简单地复制类中的每个成员。 下面看例子。   

    c++中初始化和赋值操作差别是很大的。  
    对于基本数据类型差别不大:  
    比如:  
    int a = 12; // initialization, copy 0X000C to a  
    a = 12; // assignment, copy 0X000C to a  
    但是对用户自定义的数据类型比如String 初始化和赋值就差别很大:  
    class String ...{  
     public:  
     String( const char *init ); // intentionally not explicit!  
     ~String();  
     String( const String &that );  
     String &operator =( const String &that );  
     String &operator =( const char *str );  
     void swap( String &that );  
     friend const String // concatenate  
     operator +( const String &, const String & );  
     friend bool operator <( const String &, const String & );  
     //...  
     private:  
     String( const char *, const char * ); // computational  
     char *s_;  
    };  
    初始化的构造过程比较简单:先分配一个足够大的空间然后填充上数据:  
    String::String( const char *init ) ...{  
     if( !init ) init = """";  
     s_ = new char[ strlen(init)+1 ];  
     strcpy( s_, init );  
    }  
    析构过程更简单:  
    String::~String() ...{ delete [] s_; }  

    但是如果赋值操作就复杂多了:  
    String &String::operator =( const char *str ) ...{  
      
     if( !str ) str = """";  
      
     char *tmp = strcpy( new char[ strlen(str)+1 ], str );  // 多了中间变量  
      
     delete [] s_; // 多了删除s_;  
     s_ = tmp;   // 多一个赋值操作!现在是指向字符的指针,如果是个大对象,效率的差别可想而知.  
      
     return *this;  
    }

     C++初始化语法的不一致性:

    C语言确实很优雅,整个语言的设计简洁一致。而在C++中,有一个让人诟病的问题就是变量初始化的不一致性。

       C语言中的初始化,都是用花括号进行,简单美观:

    int array[] = {1,2,3,4,5};
    struct Point point = {23};
    struct Point arrPoint[] = 
    {
      {
    2,3},
      {
    4,5},
      {
    6,7}
    };


       C++自然也兼容了C语言的初始化机制。然而,C++的Class乃至STL都不支持。它们要用不同的方式来初始化, 甚至根本不能够直接初始化, 只能使用运行时的赋值。
       比如Class:

    class Param
    {
    public:
      
    int Age;
      
    int Value;
    private:
      
    int Level;
    };

    Param param 
    = {2,3}; // ERROR
    Param param = {2,3,4}; //ERROR

       无法初始化。而如果不初始化的话,所有的成员而处于无政府状态,这显然很不让人放心。于是,C++提供了专门用于Class的初始化方式--构造函数:

    class Param
    {
    public:
      Param(
    int x, int y)
        : x_(x), y_(y)
      {}
      Param()
        : x_(
    0), y_(0)
      {}
    private:
      
    int x_, y_;
    };

    Param param(
    1,2);
    //
    Param param;

       有了构造函数,可以在构造函数的初始化列表中对成员进行初始化。可是很明显,这里头还是有一个陷阱,默认构造初始化和非默认构造初始化的调用方式是不一致的。默认构造函数不能用括号来调用,否则编译器将会发疯:

    Param param();

       它会把上面的语句看成是函数声明,而后面调用的时候就会出错,而错误信息可能会让你抓狂一下。但是这样也就算了,偏偏 new 可以接受有括号和没括号两种写法:

    Param* p1 = new Param;
    Param* p2 = new Param();

       再来说说初始化列表。初始化列表,事实上,也只能支持简单的标量类型,诸如int,bool,指针之类的;复杂点的,如数组、结构,不好意思,不支 持--只能在构造函数体中进行赋值。还有一个很迷糊初学者的问题是,成员初始化的顺序仅依赖于成员定义的顺序,而不是初始化列表中的顺序。

       再比如STL容器,这下好象更惨,连构造函数都帮不上忙了,除了初始化一个空的容器,或是复制一下别的容器,我们只能做用默认构造函数进行初始化。我们拿数组和vecotr做个比较:

    // 数组
    int arr[]={1,2,3,4};
    // vector
    vector<int> iarr;
    // 必须在某个函数中赋初值
    void init()
    {
      
    for(int i = 1; i <= 4++i) 
        iarr.push_back(i);
    }


       再复杂一点的数据结构,那单单赋值程序就要写上老长,而且还不好看。还要记得调用。这对于仅仅是简单的设置一些初值的用途来说,太过于烦琐。

       横向比较,这次好象C++还不会太落伍,只有C和动态语言提供了初始化特性,其它支持OO高级语言好象都是学C++的。如Java, C#(注C#3.0开始提供初始化功能)...
       
       C++能不能做到简洁一致的实始化呢?
       Boost的assign库做了许多有益的工作。使用assign库,至少现在可以初始化了: 

    vector<int> arr = list_of(1)(2)(3)(4);   

    typedef boost::tuple
    <int,std::string,int> tuple;
    vector
    <tuple> v = tuple_list_of( 1"foo"2 )( 3"bar"4 );

    map
    <int,int> next = map_list_of(1,2)(2,3)(3,4)(4,5)(5,6);

    stack
    <string> names = list_of( "Mr. Foo" )( "Mr. Bar")( "Mrs. FooBar" ).to_adapter();

       如果是赋值,也可以简略很多:

    vector<int> v;
    += 1,2,3,repeat(10,4),5,6,7,8,9;
    // v = [1,2,3,4,4,4,4,4,4,4,4,4,4,5,6,7,8,9]

      不过,也仅能如此了。assign经过许多努力,也仅能支持容器的初始化,而且还不够漂亮。

       C++0x已确定提供与C一致的初始化功能。 Initialer lists Initializer Lists for Standard Containers Initializer lists WP wording 等草案就是为了这个目的服务的。
       如果使用C++0x,那么程序的初始化将变得清晰和一致:

    complex<double> z = {1,2}; 
    //
    complex<double> z {1,2}; 
    // 初始化中,有等号和无等号都是允许的,下同。
    += {2,3};

    int a = {1}; 

    new vector<string>{"once""upon""a""time"}; 

    f( {
    "Nicholas""Annemarie"} ); // 参数是两个元素的列表

    return { "Norah" }; // 返回只有一个元素的列表
     
    int* e {};  // 初始化为0或NULL指针

    map
    <string,int> anim = 

      {
    "bear",4}, 
      {
    "cassovary",2}, 
      {
    "tiger",7
    }; 


       这好象是C++欠了十多年的债吧。

  • 相关阅读:
    Python基础之subprocess
    Python基础之读取ini文件
    Python如何将py文件打包成exe
    C++第四十一篇 -- 安装成功的第一个驱动文件
    C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构
    C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构
    C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载
    C++第三十七篇 -- 调试驱动程序
    iis提示“另一个程序正在使用此文件,进程无法访问。(异常来自HRESULT:0x80070020) ”解决办法
    Serv-U无法上传“中文文件夹”的问题(没有权限)
  • 原文地址:https://www.cnblogs.com/day-dayup/p/3592790.html
Copyright © 2011-2022 走看看