整理了const 指针 应用,,
const用法:
char greeting[] = "Hellow";
const char* p = greeting;//non-const pointer,const data
char const * p = greeting;//同上
char* const p = greeting;//non-const data,const pointer
const char* const p = greeting;//const data,const pointer
const出现在星号左边,表示被指物是常量;出现在指针右边,表示指针自身是常量;若两边都有,表示被指物和指针都是常量。
const优点:
(1) const与define两者都可以用来定义常量。Const定义常量类型,可进行类型检查而后者define只进行字符替换,没有类型安全检查,并且在字符替换时可能产生意想不到的错误(边际错误)。有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试。
Const float PI = 3.1415926; #define PI 3.1415926
(2)宏是字符常量,在编译阶段完成所有宏替换,同时宏消失;const同样在编译阶段发生全部替换,但是,常量名不会消失,编译器会把它放进符号表中。
(3)防止意外修改,增强程序的健壮性。Void Fun(const int i){ i=1;//错误}
(4)Const节省了空间,避免了不必要的内存分配,同时提高了效率。编译器通常不为const只读变量分配存储空间,而是将他保存在符号表中,使其成为一个编译期间的值,没有了存储与读内存的操作。const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
#define PI 3.14159 //常量宏
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中
......
double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
Const修饰函数的参数:
如果函数做输出用,不论他是什么数据类型,也不论采用指针传递还是引用传递,都不能加const修饰,否则该参数将失去输出功能。
Const只能修饰输入参数:
输入参数为指针:
如果输入参数采用“指针传递”,那么加 const 修饰可以防止意外地改动该指针,起到保护作用。
例如 StringCopy 函数: void StringCopy(char *strDestination, const char *strSource);
其中 strSource 是输入参数,strDestination是输出参数。给 strSource加上 const修饰后,如果函数体内的语句试图改动strSource 的内容,编译器将指出错误。
输入参数值传递:
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输入参数本来就无需保护,所以不要加 const 修饰。
例如不要将函数 void Func1(int x) 写成void Func1(const int x)。同理不要将函数void Func2(A a) 写成void Func2(const A a)。其中A 为用户自定义的数据类型。
非内置类型const引用:
对于非内部数据类型的参数而言,象 void Func(A a) 这样声明的函数注定效率比较底。因为函数体内将产生 A 类型的临时对象用于复制参数 a,而临时对象的构造、
复制、析构过程都将消耗时间。为了提高效率,可以将函数声明改为 void Func(A &a),因为“引用传递”仅借用一下参数的别名而已,不需要产生临时对象。但是函数void Func(A &a) 存在一个缺点:“引用传递”有可能改变参数a,这是我们不期望的。解决这个问题很容易,加const修饰即可,因此函数最终成为 void Func(const A &a)。以此类推,是否应将 void Func(int x) 改写为 void Func(const int &x),以便提高效率?完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也非常快,“值传递”和“引用传递”的效率几乎相当。
对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将 void Func(A a) 改为 void Func(const A &a)。
内置类型参数const引用:
对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如 void Func(int x) 不应该改为 void Func(const int &x)。
用 const 修饰函数的返回值:
指针返回值:
如果给以“指针传递”方式的函数返回值加 const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。
例如函数
const char * GetString(void);
如下语句将出现编译错误:
char *str = GetString();
正确的用法是
const char *str = GetString();
引用返回值:
函数返回值采用“引用传递”的场合并不多,这种方式一般只出现在类的赋值函数中,目的是为了实现链式表达。
例如
class A
{…
A & operate = (const A &other); // 赋值函数
};
A a, b, c; // a, b, c 为 A的对象
…
a = b = c; // 正常的链式赋值
const 成员函数
任何不会修改数据成员的函数都应该声明为 const 类型。如果在编写const 成员函数时,不慎修改了数据成员,或者调用了其它非const 成员函数,编译器将指出错误,
这无疑会提高程序的健壮性。
以下程序中,类 stack 的成员函数GetCount 仅用于计数,从逻辑上讲GetCount 应当为const 函数。编译器将指出GetCount 函数中的错误。
class Stack
{
public:
void Push(int elem);
int Pop(void);
int GetCount(void) const; // const 成员函数
private:
int m_num;
int m_data[100];
};
int Stack::GetCount(void) const
{
++ m_num; // 编译错误,企图修改数据成员 m_num
Pop(); // 编译错误,企图调用非 const函数
return m_num;
}
mutable解决const 成员函数不能被改变的情况。
指针和引用的区别(网上总结):
(1)引用总是指向一个对象,没有所谓的空引用。因为没有空引用,因此在使用时无需测试是否有值,而使用指针则需要测试其有效性。不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性,其次指针产生地址的copy。
void printDouble(const double& rd)
{
cout << rd; // 不需要测试rd,它
} // 肯定指向一个double值
//相反,指针则应该总是被测试,防止其为空:
void printDouble(const double *pd)
{
if (pd) { // 检查是否为NULL
cout << *pd;
}
}
(2)指针可以被重新赋值,而引用总是指向最初的值。
string s1("Nancy");
string s2("Clancy");
string& rs = s1; // rs 引用 s1
string *ps = &s1; // ps 指向 s1
rs = s2; // rs 仍旧引用s1,
// 但是 s1的值现在是
// "Clancy"
ps = &s2; // ps 现在指向 s2;
// s1 没有改变
(3)必须使用引用的场合:operator[]操作符,由于操作符必须返回能够被当做赋值对象的东西,所以需要返回一个引用。例如:str = str 1 = str2;因此必须返回对象本身。
(4)sizeof引用得到是指向对象的大小,sizeof指针是指针本身的大小。
(5)在任何情况下都不能用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。
char *pc = 0; // 设置指针为空值
char& rc = *pc; // 让引用指向空值
这是非常有害的,毫无疑问。结果将是不确定的(编译器能产生一些输出,导致任何事情都有可能发生),应该躲开写出这样代码的人除非他们同意改正错误。如果你担心这样的代码会出现在你的软件里,那么你最好完全避免使用引用,要不然就去让更优秀的程序员去做。
(6)总的来说,在以下情况下你应该使用指针,一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
如果函数采用引用方式传递,使用指针和引用作为参数类型都是可以的。使用指针和引用类型作为函数参数各有优缺点,视具体环境而定。对于引用类型,引用必须被初始化为一个对象,并且不能在使她指向其他对象,因为对引用赋值实质上是对目标对象的赋值。这是引用的缺点。优点,上边提到的也是引用的优点,因为在函数调用时需要验证引用参数的合法性。使用指针作为函数参数,用户可以随便修改指针指向的对象。其他区别就是用法的区别了,引用只需要传值,指针需要传地址。
void test(int& w)
{
int a = 1;
w = a;
}
void test1(int* w)
{
int a = 2;
w = &a;
}
void main()
{
int p = 0;
test(p);
test1(&p);
}
test通过引用传参,改变函数值。引用传参就是传的当前对象实际地址,因此通过引用传递实参其实就是传递的本身。所以通过引用进行参数传递不仅节约时间而且还节约空间。
但是当通过指针进行传参时,也是将实参的一个拷贝传递给形参,因此上例中对形参w的改变不会影响主函数p的改变。