News for 四月 2010

4、成员函数为const

【问题】一个成员函数为const有什么确切的含义呢?

1、数据意义上的const(bitwise constness)

当且仅当成员函数不修改对象的任何数据成员(静态数据成员除外)时,即不修改对象中任何一个比特(bit)时,这个成员函数才是const的。bitwise constness最大的好处是可以很容易地检测到违反bitwise constness规定的事件:编译器只用去寻找有无对数据成员的赋值就可以了。实际上,bitwise constness正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员。

[eg code]

头文件 string.h

class string
{
public:
    string(void);
    string(const char *value):lengthisvalid(false){}
    size_t length() const;
public:
    ~string(void);
private:
    char *data;

    //计算机string的长度
    size_t datalength;
    //长度当前是否合法
    bool lengthisvalid;
};

实现文件string.cpp

#include "string.h"
#include <string>
string::string(void)
{
}

string::~string(void)
{
}

size_t string::length() const
{
    if (!lengthisvalid)
    {
        datalength = strlen(data); //error
        lengthisvalid = true; //error
    }
    return datalength;
}

[自言自语]似乎这情况必须它一定要合法才行。但编译器不同意咋办呢?

[解决办法]利用c++标准组织针对这类情况专门提供的有关const问题的另一个可选方案。此方案使用了关键字mutable,当对非静态数据成员运用mutable时,这些成员的“bitwise constness”限制就被解除:

即:

class string
{
public:
    string(void);
    string(const char *value):lengthisvalid(false){}
    size_t length() const;
public:
    ~string(void);
private:
   mutable char *data;

   //计算机string的长度
   mutable  size_t datalength;
    //长度当前是否合法
   bool lengthisvalid;
};

size_t string::length() const
{
    if (!lengthisvalid)
    {
        datalength = strlen(data); //reght

        lengthisvalid = true; //right
    }
    return datalength;
}

【Other?】旧编译器不支持  mutable

类c的一个成员函数中,this指针就好象经过如下的声明:

c * const this;              // 非const成员函数中

const c * const this;        // const成员函数中

size_t string::length() const
{
  // 定义一个不指向const对象的 局部版本的this指针
  string * const localthis =
    const_cast<string * const>(this);

  if (!lengthisvalid) {
    localthis->datalength = strlen(data);
    localthis->lengthisvalid = true;
  }

  return datalength;
}

2、概念意义上的const(conceptual constness)

Edited: 四月 30th, 2010

3、const 一句话理解及在函数中的理解

[一句话]

const在*左右  则指针指向的数据为常量,如果const出现在*右边则指针本身为常量,两边都出现则二者都为常量。

class widget{};

void f1(const widget *pw) //f1取得是指向 widget常量对象的指针

void f2(widget const *pw)//跟上面是一样的,爱用哪个用哪个

【const之于函数】

const的一些强大的功能基于它在函数声明中的应用。在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。

【返回值】

让函数返回一个常量值经常可以在不降低安全性和效率的情况下减少用户出错的几率。

const rational operator*(const rational& lhs,const rational& rhs);

class string {
public:

  …

  // 用于非const对象的operator[]
char& operator[](int position)
{

     return data[position];

}

  // 用于const对象的operator[]
  const char& operator[](int position) const
{

      return data[position];

}

private:
  char *data;
};

string s1 = "hello";
cout << s1[0];                  // 调用非const
                                     // string::operator[]
const string s2 = "world";
cout << s2[0];                  // 调用const
                                      // string::operator[]

通过重载operator[]并给不同版本不同的返回值,就可以对const和非const string进行不同的处理:

string s = "hello";              // 非const string对象

cout << s[0];                    // 正确——读一个
                                      // 非const string

s[0] = ‘x’;                      // 正确——写一个
                                   // 非const string

const string cs = "world";       // const string 对象

cout << cs[0];                   // 正确——读一个
                                      // const string

cs[0] = ‘x’;                     // 错误!——写一个
                                    // const string

[问题]如果对非const operator[]的返回类型 是char本身而不是char的引用 

s[0] = ‘x’; 会出错吗??

修改一个“返回值为固定类型”的函数的返回值绝对是不合法的。即使合法,由于c++“通过值(而不是引用)来返回对象”,s.data[0]的一个拷贝会被修改,而不是s.data[0]自己,这就不是你所想要的结果了。

Edited: 四月 30th, 2010

2、const解决#define上的一些问题

另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值: #define max(a,b) ((a) > (b) ? (a) : (b))

[问题来了]

int a = 5, b = 0; max(++a, b);// a 的值增加了2次

max(++a, b+10); // a 的值只增加了1次

[egcode]

    int a = 5, b = 0;

//     max(++a, b);// a 的值增加了2次
//     cout<<a<<" "<<b<<endl;

    max(++a, b+10); // a 的值只增加了1次
    cout<<a<<" "<<b<<endl;

[自言自语]看来宏真不是好东西,但有时效率也很重要,咋办呢?用inline吧

inline int max(int a, int b) { return a > b ? a : b; }

[自言自语]不过好象只能比较int型

[自言自语]C++真强大,弄出个模板

template<class T>

inline const T&  max(const T& a,const T& b)

{

return a > b ? a : b;

}

Edited: 四月 30th, 2010

[1]、const 定义类常量

【VS2005】

class TestConst
{
public:
    TestConst(void);
public:
    ~TestConst(void);
private:
    static const int NUM_ID = 5;
    int scores[NUM_ID];
};

有VS 2005版本以上 不会出错 ,在VC6.0下报错

[VC 6环境]

class GamePlayer
{
public:
protected:
private:
    static const int num_turns /*= 5*/;  //去掉注释报错
    // int scores[num_turns];  //不能这样使用
};

只声明,但未定义,类实现文件中定义

const int GamePlayer::num_turns = 5;

旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。

在这种情况下要使用这个值该怎么做呢???

借用枚举的方式

class GamePlayer
{
public:
protected:
private:
    static const int num_turns /*= 5*/;
    // int scores[num_turns];
    enum{NUM_TRUNS = 5};
    int scores[NUM_TRUNS];
};

现在VS2010都出来了,不知道这种方法以后还能不能用到了。

Edited: 四月 30th, 2010

位域

在内存中存取数据的最小单位一般是字节,但有时存储一个数据不必用一个字节。
对于不需要一个字节来存储的数据用几个位来存储就可以了
C语言中充许一个结构体中以位为单位来使用内存,这种以位为单位的成员称为位或或位段。
eg:
struct bit_data
{
    int a:6;
    int b:4;
    int c:4;
    int d;
};
struct bit_data data;
data.b = 18;  //error
0000~1111 从0到15 而18显然超出这个范围了,但第二条语句是符合语法的。
b实际存的值是多少呢?   00010010   由于只能存4位,那么高4位被舍弃。所以实际存的值是2
若某一位段要从另外一个存储单元开始存放,结构体中的成员可以定义成如下形式
int a:6;
int b:4;
int :0;
int c:4;
ind d;
那么c是从下一个存储单位开始存放

eg:
struct bit_data
{
    int a:6;
    int b:3;
    int :8;
    int c:4;
    int d;
};
0~5  a
6~9   b
10~17  空者
18~21 放c
32~63 放d

Edited: 四月 29th, 2010