由C++复制控制想到
2011-05-18 15:31
190 查看
由C++复制控制想起 一、什么是初始化、赋值。它说初始化是创建变量并给变量赋初值,而赋值是擦除对象的当前值并用新值代替。 二、C++的初始化、赋值。在C++中进行初始化时必须使用初始化列表才叫初始化。而不是在构造函数中对其赋值。那么,初始化列表和直接赋值有什么本质区别?那下我们来看代码:
而赋值则是:
Sales_item temp3; //调用相应的无参数的构造函数。temp3 = temp1; //调用operator完成赋值。 一般来说需要复制构造函数也需要operator=。应当将二者看为一个整体。
A::A(int const var1, string const& var2) : id(var1), name(var2) { }这一次id和name就只分别调用了它们各自的复制构造函数一次,而免去了多余了那一次对operator = 的调用.对于非内置类型来说,这就提高了对象的初始化效率。虽然对于内置类型来说这没有什么大的不同,但为了保持一致性,通常也把它们放在初始化成员列表中进行初始化。 另外 = 操作符所表示的意思也不总是赋值,在对象定义声明时使用操作符 = 对象调用的就是其复制构造函数而不是operator =。
三、explicit为何物。 从《c++ primer》中的一句话说起:“某些版本将参数为一个string的构造函数定义为explicit。如果构造函数是显式的,则初始化失败;如果构造函数不是显式的,则初始化成功”。这里说的显式即是使用explicit来声明这个构造函数,而不是显式则不使用explicit。下面来看一点代码:显式使用explicit :class Sales_item{private: string str;public: explicit Sales_item(string str) { this->str = str; } void print() { cout<<str<<endl; }};void main(){ Sales_item temp= "abc.dsljf"; //① error ,不能通过编译。 Sales_item item = string("abc.dsljf"); //② error ,由无名对象初始化,不能通过编译。 Sales_item item("abcsljd"); //③ OK,可以通过编译。 temp.print();}在①、②中这时我们的右值是一个C-style的字符串,而左值是一个类。当不使用explicit 时自然是不会出现这样的情况的,可以顺利的通过编译并正常的执行。。C++作为一种强类型的语言,这里并没有显式的进行类型转换,那么一定是发生了隐式的类型转换。编译器知道传入的值是字符串,而需要的右值是Sales_item类型,但它同时也知道调用Sales_item的构造函数将字符串转换成一个合适的Sales_item。而加上explicit后,就会出现上面程序中所示的错误,那么现在为什么会是这样的情况呢?这个问题得从explicit说起。在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。只要定义了任何一个需要参数的构造函数,编译器就不会为你生成默认构造函数。就这个问题而言,在这条语句中:Sales_item item = "abc.dsljf"; 若没有在构造函数中使用explicit这个关键字,那么这个隐式转换的过程如下:
我们并没有显式的指定一个复制构造函数,这里是通过默认的复制构造函数实现这个转换过程。而当我们使用了explicit后,编译器就不允许进行隐式转换。这个时候,上面的过程不能进行。会报出如下错误: 当我们使用③,即直接构造形式时不会存在上述过程,那么就可以顺利通过。我们使用explicit的目的就是强调类型的匹配!不能随意的进行隐式转换。explicit使用注意事项:explicit 关键字只能用于类内部的构造函数声明上,并且explicit 关键字作用于单个参数的构造函数,当然,多个参数时,除首个参数外的其它参数具有默认值除外。那么再看下面的例子(感谢网友提供):按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示:class String { String ( const char* p ); // 用C风格的字符串p作为初始化值 //…}String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(“hello”);但是有的时候可能会不需要这种隐式转换,如下:
[b]下面两种写法比较正常:String s2 ( 10 ); //OK 分配10个字节的空字符串String s3 = String ( 10 ); //OK 分配10个字节的空字符串 下面两种写法就比较疑惑了:
s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。为了避免这种错误的发生,我们可以声明显示的转换,使用explicit 关键字:class String { explicit String ( int n ); //本意是预先分配n个字节给字符串 String ( const char* p ); // 用C风格的字符串p作为初始化值 //…}加上explicit,就抑制了String ( int n )的隐式转换,下面两种写法仍然正确:
下面两种写法就不允许了:String s4 = 10; //编译不通过,不允许隐式的转换String s5 = ‘a’; //编译不通过,不允许隐式的转换因此,某些时候,explicit 可以有效得防止构造函数的隐式转换带来的错误或者误解explicit只对构造函数起作用,用来抑制隐式转换。如:
当调用Function(2)的时候,2会隐式转换为A类型。这种情况常常不是程序员想要的结果,所以,要避免之,就可以这样写: class A{ explicit A(int a); }; int Function(A a);这样,当调用Function(2)的时候,编译器会给出错误信息(除非Function有个以int为参数的重载形式),这就避免了在程序员毫不知情的情况下出现错误。 四、现在来说复复制和赋值我们可以通过直接形式来初始化一个类:
当然也可以通过复制的方式进行:1)Sales_item temp2 = “I am a Chinese!”;//通过复制构造函数完成。生成一个临时对象,再复制到正在创建的对象。或者:
2)Sales_item tmp(temp2); 总之我们需要明白,复制构造函数就是: Sales_item(const Sales_itemp& it); 只要是实现了在初始化时,通过对象来赋值即是调用的复制构造函数。
而赋值则是:
Sales_item temp3; //调用相应的无参数的构造函数。temp3 = temp1; //调用operator完成赋值。 一般来说需要复制构造函数也需要operator=。应当将二者看为一个整体。
相关文章推荐
- [C/C++] 第13章 复制控制 primer
- [C/C++] 第13章 复制控制 primer
- C++复制控制:拷贝构造函数
- C++ 复制控制之复制构造函数
- C++ 复制控制
- c++学习笔记之复制控制与stl算法
- C++复制控制之复制构造函数(一)
- 【C++ rimer 学习笔记】ch13 复制控制
- C++入门学习笔记(二)--类的复制控制
- 智能指针:复制控制!《C++Primer 》第13章学习纪要
- c++ premier -- 复制控制与重载操作符
- 复制控制(学习<C++Primer>)
- C++ 复制控制
- C++复制控制基础
- C++复制控制
- 蛋疼芜鄙写的String类的c++复制控制,打基础啊打基础,打了一辈子的基础
- C++复制控制
- C++复制控制
- c++基础知识--类的复制控制(转)
- C++复制控制:赋值操作符和析构函数