为什么const对象只能访问const成员函数
C Primer中说了,在一个对象调用其成员函数时,它隐含的一个形参this指针。
例如,我们定义了一个函数CTest::ttt();实际上在编译器中该函数的定义就是CTest::ttt(CTest *const
this),该this指针所指向的内容可以改变,但是该this指针不可以被改变。当我们用CTest的一个对象test1调用ttt函数时即test1.ttt()时,编译器就会将它解释为ttt(&test1),所以我们在ttt中使用this指针就可以改变对象test1的成员变量了。
但是,当我们的对象是const对象时,即const CTest ttt;这时候表示什么意思呢,ttt的内容是不可以改边的,当我们把&ttt作为一个参数传到形参this时,矛盾出现了:ttt是一个常量,其成员不可以被改变;this指针的成员变量是可以改变的。如果我能正确的将ttt的地址传给this,那么ttt这个常量的值不是可以在this中被改变了吗,所以,编译器是不允许这种情况出现的,就提示错误了。故const对象不能访问非const成员函数。
同理,当我们的成员函数是const成员函数时,例:CTest::ttt() const,在编译器解释时会将该函数解释为CTest::ttt(const CTest * const this),this指针及其所指向的内容都不可以被修改,前面提到的矛盾也就不存在了,所以const对象可以访问const成员变量。
总结一下如下两表:
对象.成员函数
对象 | 成员函数 | 是否可行 |
---|---|---|
const | const | √ |
const | non-const | 错 |
non-const | const | √ |
non-const | non-const | √ |
成员函数调用成员函数
成员函数 | 成员函数 | 是否可行 |
---|---|---|
const | const | √ |
const | non-const | 错 |
non-const | const | √ |
non-const | non-const | √ |
篡改const对象
class String1
{
public:
String1 (const char *value)
{
data = new char[strlen(value)+1];
strcpy(data,value);
}
~String1(){delete data;}
operator char *() const
{
return data;
}
void getdata() const // 需要加上const 不然会提示错误对象包含与成员函数不兼容的类型限定符
{
cout<< data;
}
private:
char *data;
};
这个时候我们可以
const String1 B("HELLO");
String1 & alsoB=const_cast<String1&>(B);
char *str=B;
str[1]='e'; // 这样就篡改了一个const对象里的数据了。。。
B.getdata();
所以我们operator的代码需要更改
operator const char *() const
{
return data;
}
这样我们
const char *str=B; //只能这样了。。
//str[1]='e'; //此时就不能更改了
指针并不是返回内部数据句柄的唯一途径。引用也很容易被滥用。下面是一种常见的用法,还是拿 String 类做例子:
class String2
{
public:
String2 (const char *value)
{
data = new char[strlen(value)+1];
strcpy(data,value);
}
operator const char *() const
{
return data;
}
void getdata() const // 需要加上const 不然会提示错误对象包含与成员函数不兼容的类型限定符
{
cout<< data;
}
char& operator[](int index) const // 不加引用是不能赋值的~
{ return data[index]; }
private:
char *data;
};
这样也可以修改s。。。
const String2 s = "I'm not constant";
s[0]='x';
这类问题的通用解决方案和前面关于指针的讨论一样:或者使函数为非 const,或者重写函数,使之不返回句柄。 并不是只有 const 成员函数需要担心返回句柄的问题,即使是非 const 成 员函数也得承认:句柄的合法性失效的时间和它所对应的对象是完全相同的。 这个时间可能比用户期望的要早很多,特别是当涉及的对象是由编译器产生的临时对象时。
所以,对于 const 成员函数来说,返回句柄是不明智的,因为它会破坏数据抽象。对于非 const 成员函数来说,返回句柄会带来麻烦,特别是涉及到临 时对象时。句柄就象指针一样,可以是悬浮(dangle)的。所以一定要象避免 悬浮的指针那样,尽量避免悬浮的句柄。
例如:
String2 some()
{
return "asdf";
}
............
const char *p = some();
cout<<p;
因为当你想打印 pc 所指的字符串时,字符串的值是不确定的。造 成这一结果的原因在于 pc 初始化时发生了下面这些事件: 1. 产生一个临时
String 对象用以保存 someFamousAuthor的返回值。 2. 通过 String 的 operator const
char*成员函数将临时 String 对象转换为 const char*指针,并用这个指针初始化 pc。 3. 临时 String
对象被销毁,其析构函数被调用。析构函数中,data 指针被删除 (代码详见条款 11)。然而,data 和 pc
所指的是同一块内存,所以现在 pc 指 向的是被删除的内存——–其内容是不可确定的。