const用法
1)定义一个常量,可以很方便地进行参数的调整和修改。const对象一旦创建后其值不能被修改,所以const对象必须初始化。
2)const变量相对于宏定义更安全。编译器可以对const变量进行类型安全检查,而对宏定义只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。
3)可以保护被修饰的东西,防止被意外修改,增强程序的健壮性。如:void fun(const string& str)
4)为函数重载提供了一个参考
struct A
{
void fun(void) {cout<<”void fun(void)”<<endl;} //一个函数
void fun(void) const { cout<<”void fun(void)const”<<endl;} //上一个函数的重载
};
A a1;const A a2;
a1.fun(); // void fun(void)
a2.fun(); // void fun(void) const
5)可以节省空间,避免不必要的内存分配。const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数。所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
const inti = 0; //此时不会分配空间
int *j =(int *) &i; //分配空间
*j = 1;
printf("%d,%d", i, *j); // 0, 1 原因:i在编译时直接被替换,指针j指向分配的空间
6)const的引用
a) 对常量的引用(reference to const),简称常量引用。对常量的引用不能被用作修改它所绑定的对象
const int ci = 1024; const int &r1 = c1;
b) 对const的引用可能引用一个非cconst的对象
常量引用仅对引用可参与的操作做出限定,对于引用的对象本身是不是一个常量未作限定。因为对象也可能是个非常量,所以允许通过其他途径改变它的值。
int i = 42; cons tint &r2= i;
7)指针和const
a) 指向常量的指针(pointer to const):不能用于改变其指向的值。要想存放常量对象的地址,只能使用指向常量的指针:
const int pi = 10; cons tint *cptr = pi; //允许不赋初始值
和常量引用一样,指向常量的指针也没有规定其所指向的对象必须是一个常量。
b) const指针(常量指针 const pointer):必须初始化,一旦初始化,其值不能在改变。
int num = 10; int *const pNum = #
指针本身是一个常量并不意味着不能通过指针修改其所指对象的值,能否这样做完全依赖于所指向的对象。
扩展:顶层const(top-level-const)和底层cosnt(low-level-const)
顶层const:表示指针本身是个常量;低层cosnt:表示指针所指的对象是一个常量。
顶层cosnt可以表示任意的对象是常量,对任何数据类型都适用,如算数类型、类和指针等。
底层const则与指针和引用等复合类型的基本类型部分有关。
int i =0;
int*const p1 = &i; //不能改变p1的值,顶层const
const intci = 42; //不能改变ci的值,顶层const
cons tint*p2 = &ci; //允许改变p2的值,底层const
const int*const p3 = p2; //靠右的const是顶层const,靠左的const是底层cosnt
const int&r = ci; //用于声明引用的const都是底层const
(1)当执行对象拷贝时,常量是底层const和顶层const区别明显。其中,顶层const不受影响:
i = ci; //正确:拷贝ci的值是一个顶层const,对此操作无影响
P2 =p3; //正确:p2和p3指向的对象类型相同,p3顶层const的部分不影响
(2)底层的const的限制却不能忽视。当执行对象的拷贝时,拷入和拷出的对象必须具有相同的底层const资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行:
int *p =p3; //错误:p3包含底层const的定义,而p没有
p2 = p3; //正确:p2和p3都是底层const
p2 =&i; //正确:int*能转换成const int*
int&r = ci; //错误:普通的int&不能绑定到int 常量上
const int&r2 = i; //正确:const int&可以绑定到一个普通int上
C++中四种类型转换方式
1) static_cast:任何具有明确定义的类型转换,只要不包括底层const,都可以使用static-cast。主要用于基本类型间的相互转换,和具有继承关系间的类型转换(将子类类型的指针转换为父类类型的指针,也能够转换父类类型的指针为它的子类类型的指针)。
a) 当把一个较大的算数类型赋值给较小类型时,static_cast非常有用。
double i = 10.3; int num =static_cast<int>(i);
b) 对于编译器无法自动执行的类型转换也非常有用。
int d = 10; void*p = &d; int *dp =static_cast<int*>(p);
2) const_cast:只能改变运算对象的底层const
const char *pc;
char *p =const_cast<char*>(pc); //正确:但通过p写值是未定义的行为
对于将常量对象转换为非常量对象的行为,称其为“去掉const性质(cast away the const)”。一旦去掉某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的;然而如果对象是一个常量,在使用const_cast执行写操作就会产生未定义的后果。
const_cast常常用于由函数重载的上下文中。
3) reinterpret_cast:通常为对象的位模式提供较低层次上的重新解释。将一个类型的指针转换为另一个类型的指针,它也允许从一个指针转换为整数类型。这种转换不用修改指针变量值数据存放格式(不改变指针变量),而是在编译时重新解释指针的类型(操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换)。但不能用于非指针类型的转换。
4) dynamic_cast:只能在继承类对象的指针之间或引用的相互转换,且通常情况下应含有虚函数,在运行时动态转换。只用于对象的指针和引用。当用于多态类型时,它允许任意的隐式类型转换以及相反过程,会检查操作是否有效。指针间转换失败可以通过判断是否为null来判断,引用转换失败则抛出 bad_cast 异常。
注:当知道派生类的具体类别时,使用static_cast效率更高。
静态成员变量、非静态成员变量,静态方法和非静态方法的区别
1)静态成员变量:在C++中,静态成员变量是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用。静态成员变量使用前必须先初始化。
2)非静态成员变量:不可以直接使用类名访问,可以用实例化来访问,每个对象均有一份独立的值。
3)静态成员函数中不能调用非静态成员,只能调用静态成员变量。
4)非静态成员函数中可以调用静态成员。因为静态成员属于类本身,在类的对象创建之前就已经存在了,所以在非静态成员函数中是可以调用静态成员的。
静态成员可以通过双冒号来使用即<类名>::<静态成员名>
class A
{
public:
static int num;
int data;
A(int i=0):data(i){++num; ++data;}
static int getNum(){return num;}
};
int A::num = 0;
int main(void)
{
A a;
cout<<"A::num="<<A::getNum()<<" data="<<a.data<<endl; //A::num=1 data = 1;
A b;
cout<<"A::num="<<A::getNum()<<" data="<<a.data<<endl; //A::num=2 data = 1;
return 0;
}