C++笔记(1)explicit构造函数

来源:互联网 发布:直通车软件哪个好 编辑:程序博客网 时间:2024/05/16 04:44

explicit构造函数
  最近在工程里看到过explicit这个关键字,查了下作用,但一直也没有去实践下到底是什么样子。
  用一个小程序测试了下,具体过程如下。
  由于按照默认规定,只有一个参数的构造函数也定义了一个隐式转换,将该构造函数对应数据类型的数据转换为该类对象,如下面所示:
class String {
  String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}
String s1 = “hello”; //OK 隐式转换,等价于String s1 = String(“hello”);
但是有时候可能不需要这种隐式转化,如:
class String {
  String( int n ); // 本意是预先分配n个字节给字符串
  String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}

下面两种写法比较正常:
String s2 ( 10 ); //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串
下面两种写法就比较疑惑了:
String s4 = 10; //编译通过,也是分配10个字节的空字符串
String s5 = ‘a’; //编译通过,分配int(‘a’)个字节的空字符串,a的ascii码值

s4 和s5 分别把一个int型和char型,隐式转换成了分配若干字节的空字符串,容易令人误解。
为了避免这种错误的发生,可以声明显示的转换,使用explicit 关键字:
class String {
explicit String ( int n ); //本意是预先分配n个字节给字符串,避免隐式转化
  String ( const char* p ); // 用C风格的字符串p作为初始化值
//…
}
下面两种写法仍然正确:
String s2 ( 10 ); //OK 分配10个字节的空字符串
String s3 = String ( 10 ); //OK 分配10个字节的空字符串

下面两种写法就不允许了:
String s4 = 10; //编译不通过,不允许隐式的转换
String s5 = ‘a’; //编译不通过,不允许隐式的转换

explicit 只对构造函数起作用,用来抑制隐式转换。如:
class A
{
  A(int a);
}
Int Function(A a);
  当调用Function(2)的时候,2会隐式转换为A类型。这种情况常常不是程序员想要的结果,所以,要避免之,就可以这样写:
class A {
explicit A(int a);
};
int Function(A a);

这样,当调用Function(2)的时候,编译器会给出错误信息(除非Function有个以int为参数的重载形式),这就避免了在使用者在毫不知情的情况下出现错误。

C++笔记(2) list与vector
  list封装了链表,vector封装了数组, list和vector得最主要的区别在于vector使用连续内存存储的,他支持[]运算符,而list是以链表形式实现的,不支持[]。
vector对于随机访问的速度很快,但是对于插入尤其是在头部插入元素速度很慢,在尾部插入速度很快。list对于随机访问速度慢得多,因为可能要遍历整个链表才能做到,但是对于插入就快的多了,不需要拷贝和移动数据,只需要改变指针的指向就可以了。另外对于新添加的元素,Vector采用的是从尾部追加,而List可以任意加入,同时可以高效的插入删除元素。list不支持随机访问。所以没有 at(pos)和operator[]。
  查看MSDN和网上相关资料模仿实现简易的vector时因为对于template和typedef不熟导致了错误。查了很多资料没有搞明白。模板还是要扎扎实实看一下啊!问题在于使用typedef时候就有错,而且乱七八糟的。
  所以我认为要想完全搞明白这些容器还是需要看下STL源码,得深度剖析。
  未完待续……
C++笔记(3) 参数传递方式
  C++中的参数传递有三种方式:传递变量名,传递指针,传递引用。即值传递,指针传递和引用传递。
  将变量名作为形参和实参
  这种情况下传给形参的是变量的值,传递是单向的,就是说如果在执行函数期间形参的值发生变化,并不会传回给实参,这就是所谓的值传递。因为在调用函数期间,形参和实参并不是同一个存储单元。简单示例如下:
  #include “stdafx.h”
  #include
  using namespace std;
  int _tmain(int argc, _TCHAR* argv[])
  {
void swap(int,int);//参数为整型变量
int i=3,j=4;
cout<<”i=”<

#include <iostream>  using namespace std;  int _tmain(int argc, _TCHAR* argv[])  {void swap(int*,int*);//参数为整型指针变量int i=3,j=4;cout<<"i="<<i<<",j="<<j<<endl;swap(&i,&j);//变量地址cout<<"i="<<i<<",j="<<j<<endl;system("PAUSE");return 0;  }  void swap(int *p1,int *p2){//形参为整型指针变量int temp;temp=*p1;*p1=*p2;*p2=temp;  }

  结果:
  i=3,j=4
  i=4,j=3
  请按任意键继续…
  引用传递
  引用形参是把实参的地址传递给形参(引用变量),从而是形参的地址取实参的地址,从而达到形参与实参共享同一单元的方式,这就是地址传值方式。
  方式2中传递指针变量需要额外开辟一块内存单元,内容为地址。而方式3不是一个独立变量,不需要独占内存单元。而且调用使用引用形参的函数时,实参不必用函数的地址,而是直接使用变量名。系统向形参传递的是实参的地址而不是实参的值。
  这三种方式有什么区别呢?
  1.从功能上。按值传递在传递的时候,实参被复制了一份,然后在函数体内使用,函数体内修改参数变量时修改的是实参的一份值拷贝,而实参本身是没有改变的(例如上面的示例1),所以如果想在调用的函数中修改实参的值,使用值传递是不能达到目的的,这时只能使用引用或指针传递。
  2.从传递效率上来说。这里所说传递效率,是说调用被调函数的代码将实参传递到被调函数体内的过程,正如上面代码中,这个过程就是函数main()中的a,b传递到函数swap()中的过程。这个效率不能一概而论。对于内建的int,char,,short,long,float等4字节或以下的数据类型而言,实际上传递时也只需要传递1-4个字节,而使用指针传递时在32位CPU中传递的是32位的指针,4个字节,都是一条指令,这种情况下值传递和指针传递的效率是一样的,而传递double,long long等8字节的数据时,在32位CPU中,其传值效率比传递指针要慢,因为8个字节需要2次取完。而在64位的CPU上,传值和传址的效率是一样的。再说引用传递,这个要看编译器具体实现,引用传递最显然的实现方式是使用指针,这种情况下与指针的效率是一样的,而有些情况下编译器是可以优化的,采用直接寻址的方式,这种情况下,效率比传值调用和传址调用都要快,与上面说的采用全局变量方式传递的效率相当。再说自定义的数据类型,class,struct定义的数据类型。这些数据类型在进行传值调用时生成临时对象会执行构造函数,而且当临时对象销毁时会执行析构函数,如果构造函数和析构函数执 行的任务比较多,或者传递的对象尺寸比较大占用的内存空间也比较大,那么传值调用的消耗就比较大。这种情况下,采用传址调用和采用传引用调用的效率大多数下相当,正如上面所说, 某些情况下引用传递可能被优化,总体效率稍高于传址调用。
  3.从执行效率上讲。这里所说的执行效率,是指在被调用的函数体内执行时的效率。因为传值调用时,当值被传到函数体内,临时对象生成以后,所有的执行任务都是通过直接寻址的方式执行的,而指针和大多数情况下的引用则是以间接寻址的方式执行的,所以实际的执行效率会比传值调用要低。如果函数体内对参数传过来的变量进行操作比较频繁,执行总次数又多的情况下,传址调用和大多数情况下的引用参数传递会造成比较明显的执行效率损失。
  综合2、3两种情况,具体的执行效率要结合实际情况,通过比较传递过程中的资源消耗和执行函数体消耗之和来选择哪种情况比较合适。而就引用和指针传递的效率上来讲,引用传递的效率始终不低于指针,所以从这个层面来讲,在C++中使用参数传递时应该优先考虑使用引用传递而非指针。
  4.从类型安全上来讲。值传递和引用传递在传递过程中都会进行强类型检查,而指针的检查较弱,特别是当类型被声明为void*时它几乎不检查,只要是指针编译器就认为是合法的。所以导致程序的健壮性下降。如果没有必要,就使用值传递和引用传递,最好不用指针传递,更好地利用编译器的类型检查,使得我们有更少的出错机会,以增加代码的健壮性。
  5.从参数检查上讲。一个健壮的函数,总会对传递来的参数进行参数检查,保证输入数据的合法性,以防止对数据的破坏并且更好地控制程序按期望的方向运行,在这种情况下使用值传递比使用指针传递要安全得多,因为你不可能传一个不存在的值给值参数或引用参数,而使用指针就可能,很可能传来的是一个非法的地址(没有初始化,或者指向已经delete掉的对象的指针等)。所以使用值传递和引用传递会使你的代码更健壮,具体是使用引用还是使用,最简单的一个原则就是看传递的是不是内建的数据类型,对内建的数据类型优先使用值传递,而对于自定义的数据类型,特别是传递较大的对象,那么请使用引用传递。
6.从灵活性上来说。无疑,指针是最灵活的,因为指针除了可以像值传递和引用传递那样传递一个特定类型的对象外,还可以传递空指针,不传递任何对象。这算是指针的优点了。
  在参数传递过程中配合使用较多的const关键字,可以保证参数不被修改。就像我找人帮我数钱,我肯定不会希望我的钱变少了,所以使用const修饰变量,使其指向一个const对象。需要注意下参数顺序。当同个函数名有不同参数时,如果有相同的参数尽量要把参数放在同一位置上,以方便其他函数调用。

0 0
原创粉丝点击