随笔 - 55  文章 - 15  trackbacks - 0
<2012年5月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

1. 取代#define 进行值替代
2. 修饰指针
3. 修饰参数和返回值
4. 修饰类和类成员变量及类成员函数


一、值替代
      #define只在预处理器进行值替代,并且没有类型标识,出现错误很难排查。const 提供类型信息,在编译期可以进行类型检查。编译器在编译时可以通过必要的运算把一个复杂的常量表达式简化,称为常量折叠。
       我理解的常量折叠是:编译器会把这个常量放到一个符号表中,比如说const int i = 1;符号表中有一个符号i,同时对应了一个数值1,此时是不分配内存空间的。如果你强行分配内存空间的话也可以。
const int i = 1;
int* ip = const_cast<int*>(&i);
*ip = 2;
cout <<"i = "<<i ;
cout <<"*ip = "<< *ip;
cout <<"*(&i) = "<<*(&i) ;
// i = 1; 
//*ip = 2;
// 
*(&i) = 1;
不论你怎么通过内存修改i,看起来i有两个值,1和2,这貌似就是常量折叠。
内部链接问题
  C++中的const是内部链接,即const仅在被定义过的文件中才是可见的,链接时不能被其他编译单元看到。在定义时,必须赋一个初值,不然必须用extern显式地声明。
通常,编译器是不为const创建存储空间的,相反把这个定义保存在它的符号表中。但是extern强制执行了存储空间的分配,因为extern表示使用外部链接,也就是有几个不同的编译单元可以引用它,所以必须有存储空间。(这句话我的理解是:每个编译单元都有自己的符号表,其他编译单元是不能看到其他编译单元符号表中的东西的。其他编译单元如果要引用一个外部变量,该变量必须是在内存中,可以读取的。所以一定要分配空间。外部链接一般是找到相应名字的内存地址,比如链接一个外部函数,就是找到这个函数的内存地址)
  通常没有extern的时候,不会分配存储空间。但是有些结构比较复杂的const,编译器还是会分配空间。因为.h文件中一般放置的都是不分配空间的代码,所以cosnt变量默认为内部链接。不然链接程序在多个目标文件中看到相同的定义就会”抱怨“。

用常量表达式定义数组大小
  定义数组的大小的时候,必须是常量表达式,不能为变量,至于为什么。不太清楚,只是编译器在编译的时候给出错误:期望一个常量表达式,不能分配一个长度是0的数组,变量是长度未知的。(我的理解是,因为变量是变的,如果静态地声明了一个数组a[b]因为b确定,定义的时候b是1,在之后又被修改为2,那数组a长度到底应该是1呢还是2呢?)
  常量表达式:
  const int i = 100;// constant
      const int j = 100 + i;// constant express
      long addr = (long)&j;// forces storage
     char buf[j + 10];//right
     const可以应用于集合,但是编译器不会把一个集合保存到符号表中,所以必须分配内存。这种情况下,const意味着‘不能改变的一块内存’,编译器不需要知道里面存储的内容。如果这样理解的话,那么编译器在看到int b = 1;这句话的时候,也会想”这是一个int型的变量,我给他分配2个字节就行了,里面是什么,或许要到运行的时候再说了“,所以a[b]的长度就是未知长度了。

二、const 指针和指向const的指针
  int* u, v;== int*u; int v;
      因为c++对类型检查非常精细,所以,你可以把一个非const指针赋给const指针,但是不能把一个const指针赋给非cosnt指针。同样也不能把指向const的指针赋给指向非const的指针
int d = 1;
const int e = 2;
int* u = &d;//right;
int* v = &e;//wrong
int* w = const_cast<int*>(&e);//right
int* y = (int*)&e;//right


考虑下面一句代码:
char* cp = "howdy";
”howdy"是存放在字符常量区的,它是个常量,这个表达式返回的是一个const char*,但是在某些编译器中是可以通过编译的。但最好写成const char* cp = "howdy";修改该字符串数组是错误的。如果想修改它就把它放到一个数组中char cp[] = "howdy";为什么呢?
char cp[] = "abcd";
cp[1] = '2';
cout <<cp << endl;
// legall 

char *cp = "abcd";
cp[1] = '2';
cout << cp << endl;//cause runtime error

第一种情况:"abcd"是放在字符常量区的,但是数组是放在栈上的,现在栈上有5个字节的数组,分别存放了'a','b','c','d''/0',所以你修改它没问题。第二种:"abcd"是放在字符常量区的,cp只是一个指针,指向了这串字符串,想修改它是错误地。

临时量
  他们也需要存储空间,并且可以构造和销毁,但是,我们看不到它们,并且自动地成为常量。一旦计算结束,临时变量也会不复存在,所以对临时变量所做的任何操作都会丢失。
传递指针和引用的时候一般都用const修饰。
值拷贝:
int i = 1;
intconst ip = &i;
int* ip2 = ip;//right;because you can't modify ip by ip2

const int j = 2;
int k = j;//right;// you can't modify j by k;

const int m = 1;
int* mp = &m;//wrong, you can modify m by mp, but m is a constant

类中的const
非static变量在构造函数的初始化列表里定义
static const 在定义的地方初始化,enum可以实现static const作用
class A
{
  enum {size = 100};
int i[size];
};//equal to static const int size = 100;

const对象
const A a(1); 必须保证对象的数据成员在其声明周期中不被改变。如何保证?用const成员函数,明确表示不改变成员数据。声明的时候要用const 定义时也要用const,不然会被看作两个函数。
class X
{
int i;
public:
int f() const;
};

int X::f() const 
{
 return i;
}

按位const和按逻辑const
按位是指这块内存中的每个字节都是不能改变的。
按逻辑是指,可以在某个成员函数中修改成员变量。两种方法:

class X
{
  int i;
public:
  void f() const;
}

void X::f() const
{
  (const_cast<X*>(this))->i++;//right
}

void X::f(const X* this)
{
   this->i++;//illegal
  (const_cast<X*>(this))->i++;//right
}

//////////another method//////////
class X
{
  mutable int i;
public:
  void f() const;
}

void X::f() const
{
  i++;//right
}
posted on 2012-05-30 16:12 Dino-Tech 阅读(162) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理