构造函数可以根据参数类型不同区分,也可以根据参数个数不同区分,但如果一个构造函数的参数有缺省值而且前面的参数与其它构造函数相同,那么如果在调用的时候用到了缺省值,编译器无法分辨该调用那个函数,会报错.
可以在定义函数的时候,让某个参数只有类型名而没有标识符,这样做的目的是为了将来可能需要插入一个参数,在调用的时候随便给这个位置一个值就可以了.
const int* p;int const* p;指向常量的指针,它的内容(*p)不能被改变,也不能将它的值赋int * const p,常指针,值不变。给非常量指针。对临时对象要使用常值引用接收,因为临时对象是常量。
对于const成员变量,必须在构造函数前(构造函数初始化表)对它赋初值(莄onst也可以在这里赋值,但没有必要,可以转到构造函数体内,虽然在初始化表里效率更高)。任何类型的成员变量都不能在声明的时候赋值。
burn *p="123456";cout<<*p++<<*p++;*p=3;burn *t;t=p;输出结果为:21,因为求值顺序是从右到左,第三个语句是错的,*p的内存是不可写的,似乎p应该为一个常量指针,但确实可以将p赋给一个非常指针,语句5不报错.
如果成员函数被声明为const,那么其中不能含有改变成员变量值的语句(但可以改变被mutable修饰的成员变量)),也不能调用非const成员函数,如果对象被声明为const,那么它只能调用const成员函数.
volatile的用法同const,甚至可以使用const volatile做修饰没,volatile标识数据可能被别的进程改变,因此有必要在每次使用的时候重读这个数据,这在优化期间特别重要,防止编译器做一些假设.
给宏的参数最好是简单变量,如果不是,如a++,那么变量在宏中出现几次,a就会被加多少次。最好不用宏做类似函数的事情,在类中,用内联函数代替宏,一样可以得到高效率。
一个程序的所有文件,所有的名字(不在函数或类中)缺省都是外部连接,这意味着不同文件中相同的名字(不在函数或类中)会引起冲突,如果对这些名字用static修饰,就会变成内部连接,名字仅在编译单元内可见。extern是static的反义词,表示外部连接,同缺省的意义相同。两种连接的名字都会存储在静态存储区。
调用c库,在声明的时候要使用extern "C" 函数声明,指明这是一个c连接.因为c++同c的编译器不同,会为函数产生不同的内部名,按照c++的方式连接c函数,会找不到库中的函数体.当然,通常情况下库的开发上已经为我们做好了这些.
void inf(int*&i){i++;} 调用:int *i=0; inf(i); 上面的函数是以指针引用做参数,改变指针的值.还可以使用指向指针的指针,要麻烦一些,不过表达更明确:void inf(int **i){(*i)++}; 调用时:int *i=0; inf(&i);
通过值传递给函数,或者函数返回一个对象,是使用位拷贝建立对象,这种情况下编译器会调用拷贝构造函数(如果没有编译器会建立一个缺省的),但对象销毁时会调用析构函数.
如果想禁止通过值传递某个对象,只要声明一个私有的拷贝构造函数,此时编译器认为用户接管了这项工作,不会建立缺省的拷贝构造函数,而用户建立的函数是私有的,没法调用,编译器就会报错.
一个指向函数的指针:void inf(int *&i){i++;}int main(int argc char* argv[]){int *i=0;cout<<i<<endl;void (*pf)(int *&);pf=&inf;(*pf)(i);cout<<i<<endl;}
运算符重载重载仅是对用户类型的数据来说的,对内置的数据类型是不可以重载运算符的。.和.*都不能重载.可以将运算符重载看作另外一种形式的函数调用,函数的名字是operator@,@代表运算符,参数的个数取决于两个因素:1 运算符是一元还是二元2 运算符是全局函数(一元是一个参数,二元是两个参数),还是成员函数(一元没有参数,二元一个参数----对象变为左侧参数)
重载运算符的返回值是否常量:当返回的是一个临时值得时候,如:%,&,>>,这些运算符得到的结果要赋给另外一个变量,这时返回值是const,如果返回值直接用于变量,如-=,+=,这是返回值不要加const.
函数返回对象的时候,返回一个临时对象比新建一个对象在返回效率要高很多,因为这时调用的是普通构造函数而不是拷贝构造函数,而且不需要调用析构函数,虽然新建一个对象再返回返回的也是一个临时对象.
自动类型转换:可以编程实现自动类型转换.如需要从对象one到two,那么只需要为two定义一个以one&为参数的构造函数,当编译器发现需要进行从对象one到two的转换的时候,会自动检查two的定义,找到这个构造函数,构造一个two对象.如果需要显式类型转换,在构造函数前加一个:explicit
还有一种自动类型转换方法是:为需要转换的对象重载一个运算符,运算符以要转换到的对象的名字命名.无须声明返回值 operator one() const{ go one(x);}
不过并不提倡隐式类型转换,这样容易隐藏错误,也会降低调用时的效率. 使用全局重载运算符而不是成员运算符的好处是可以对左右操作书都自动作类型转换,而成员运算符的操作数左侧的必须是正确的对象
return arrange(s1+s2); 与arrange temp(s1+s2);go temp;的效率是不同的,后者要进行对象拷贝,而前者直接将临时对象创建在函数的返回区。同时也更加简洁。
如果不给类定义拷贝构造函数和赋值函数,如果类中有指针变量,就会导致错误,如果指针指向动态内存区,那这块内存会丢失,而两个指针相同一个块内存,导致其值无法判定,而且两个函数的析构函数会将这块内存释放两次,导致出错。arrange a("hello");arrange b("world");arrange c(a); //调用拷贝构造函数,还可以写成:arrange c=a;但风格较差。c=a; //调用赋值函数(operator =) 赋值函数中注意先检查自赋值。
在继承当中,构造函数,析构函数,赋值函数都不能被继承,在编写子类时要注以下几点:1子类必须在构造函数的初始化表调用基类的构造函数。2父类和子类的析构函数都必须是virtual.//用于多态。3子类赋值函数要调用父类的赋值函数:Base::operater=(other);
重载new和remove的原因有两个:需要反复分配内存,需要亲自做这个工作提高效率,还有就是减少内存碎片,比如可以首先使用静态成员指针保留很大一块内存(在静态存储区),在其中完成内存的分配,并自己标记分配和释放,释放的时候,只是标记内存,而不remove释放。重载的new和remove只完成内存的分配和回收工作。new接受coat_t函数,完成内存的分配,返回一个cancel*指针,remove接受一个cancel*指针,将它释放。注意重载new和remove有两种不同的形式,一个用于每次创建一个对象,另一个用来创建一个对象数组,需要加上[]。如果重载了前者,那么在创建对象数组的时候,系统会调用全局的new和remove.
异常处理函数会首先调用所有在try块中创建了的对象的析构函数,然后执行异常处理函数,然后继续运行后面的程序。但问题是,如果一个析构函数出现了异常,在析构函数中异常前创建的堆上的所有对象都无法调用其析构函数正常销毁。方法是使用模板,并自初始化表创建这些模板对象。
set_unexpceted可以截获没有被函数异常规格说明包括得异常,还可以简单的用一个impel;将这个异常作为已知异常再次抛出,如果有相应的surprise语句,那么就可以捕获这个异常。
抛出异常的子类,会被能够捕获其父类异常的处理器捕获。这时会产生切片,即处理器收到的是一个父类,使用引用而不是传递值可以避免这个问题。.
Related article:
http://www.cppblog.com/zzh/archive/2007/08/13/29864.html
comments | Add comment | Report as Spam
|