条款29:避免传回内部数据的handles

1,考虑下面的代码:
class string
{
operator char*() const;
...
}

const String B("Hello World");
char* str = B;//调用B.operator char*()
strcpy(str, "Hi Tom");

3,一个快速但是不正确的实现:
inline String::operator char*() const
{
return data;
}
注:这个handler给了调用者无限制使用"私有字段data所指目标之物"的权利.
如下图所示:

[img]http://dl.iteye.com/upload/attachment/256527/9504562c-307b-384e-8eb3-47388e0e3b05.png[/img]

实例代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
String(const char* value);
String(const String& rhs);
~String();
String& operator=(const String& rhs);
friend ostream& operator << (ostream& out, const String str);
operator char*() const;
private:
char* data;
};

inline String::String(const char* value)
{
if (value)
{
data = new char[strlen(value)+1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}

inline String::String(const String& rhs)
{
//这里data第一次被分配空间
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
}

inline String::~String()
{
delete[] data;
}

inline String& String::operator=(const String& rhs)
{
if (this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}

ostream& operator << (ostream& out, const String str)
{
return out << str.data;
}

//这里应该避免,传回内部数据的handler,内部数据可能会被修改

inline String::operator char*() const
{
return data;
}

int main()
{
const String B("Hello World");
cout << B << endl;
char* str = B;//调用B.operator char*()
strcpy(str, "Hi Tom");
cout << B <<endl; //私有数据被改变了
return 0;
}

4,一个比较慢但是比较安全的做法:
inline String::operator char*() const
{
char* copy = new char[strlen(data)+1];
strcpy(copy, data);
return copy;
}
注:代价,函数调用者必须记得将获得的指针delete.

实例代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
String(const char* value);
String(const String& rhs);
~String();
String& operator=(const String& rhs);
friend ostream& operator << (ostream& out, const String str);
operator char*() const;
private:
char* data;
};

inline String::String(const char* value)
{
if (value)
{
data = new char[strlen(value)+1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}

inline String::String(const String& rhs)
{
//这里data第一次被分配空间
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
}

inline String::~String()
{
delete[] data;
}

inline String& String::operator=(const String& rhs)
{
if (this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}

ostream& operator << (ostream& out, const String str)
{
return out << str.data;
}

//一种比较慢的版本,但是要手工delete.
inline String::operator char*() const
{
char* copy = new char[strlen(data)+1];
strcpy(copy, data);
return copy;
}

int main()
{
const String B("Hello World");
cout << B << endl;
char* str = B;//调用B.operator char*()
strcpy(str, "Hi Tom");
cout << B <<endl;
delete str;
return 0;
}

5,另一个稍有不同的策略:传回一个指向const char的指针.
inline String::operator const char*() const
{
return data;
}
实例代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
String(const char* value);
String(const String& rhs);
~String();
String& operator=(const String& rhs);
friend ostream& operator << (ostream& out, const String str);
operator const char*() const;
private:
char* data;
};

inline String::String(const char* value)
{
if (value)
{
data = new char[strlen(value)+1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}

inline String::String(const String& rhs)
{
//这里data第一次被分配空间
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
}

inline String::~String()
{
delete[] data;
}

inline String& String::operator=(const String& rhs)
{
if (this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}

ostream& operator << (ostream& out, const String str)
{
return out << str.data;
}

//这里应该避免,传回内部数据的handler,内部数据可能会被修改

inline String::operator const char*() const
{
return data;
}

int main()
{
const String B("Hello World");
cout << B << endl;
const char* str = B;//调用B.operator const char*()
strcpy(str, "Hi Tom"); //显式说明,不能改变
return 0;
}

6,指针并不是"传回内部资料之handler"的唯一途径.
考虑下面的代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
String(const char* value);
String(const String& rhs);
~String();
String& operator=(const String& rhs);
friend ostream& operator << (ostream& out, const String str);
operator const char*() const;
char& operator[](int index) const { return data[index]; }
private:
char* data;
};

inline String::String(const char* value)
{
if (value)
{
data = new char[strlen(value)+1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}

inline String::String(const String& rhs)
{
//这里data第一次被分配空间
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
}

inline String::~String()
{
delete[] data;
}

inline String& String::operator=(const String& rhs)
{
if (this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}

ostream& operator << (ostream& out, const String str)
{
return out << str.data;
}

//这里应该避免,传回内部数据的handler,内部数据可能会被修改

inline String::operator const char*() const
{
return data;
}

int main()
{
String s = "I'm not constant";
s[0] = 'x';
cout << s << endl;
const String cs = "I'm constant";
cs[0] = 'x';
cout << cs << endl; //这里const对象指向的数据被改变了
return 0;
}

解决之道:
(1)让函数成为non-const;
(2)重写函数,不要让它传回任何handle.

7,即使是non-const member functions,也必须面对这个事实:handle的有效性将在它所对应的那个对象终了时结束.
考虑下面的代码:

#include <iostream>
#include <string.h>

using namespace std;

class String
{
public:
String(const char* value);
String(const String& rhs);
~String();
String& operator=(const String& rhs);
friend ostream& operator << (ostream& out, const String str);
operator const char*() const;
char& operator[](int index) const { return data[index]; }
private:
char* data;
};

inline String::String(const char* value)
{
if (value)
{
data = new char[strlen(value)+1];
strcpy(data, value);
}
else
{
data = new char[1];
*data = '\0';
}
}

inline String::String(const String& rhs)
{
//这里data第一次被分配空间
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
}

inline String::~String()
{
delete[] data;
}

inline String& String::operator=(const String& rhs)
{
if (this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}

ostream& operator << (ostream& out, const String str)
{
return out << str.data;
}

//这里应该避免,传回内部数据的handler,内部数据可能会被修改

inline String::operator const char*() const
{
return data;
}

String someFamousAuthor()
{
return "Stephen King";
}

int main()
{
cout << someFamousAuthor() << endl;
const char* pc = someFamousAuthor();
cout << pc << endl; //pc指向的区域已经被delete.
return 0;
}

发生的事情:
(1)产生一个暂时性的String object,用来放置someFamousAuthor()传回值.
(2)经由operator const char* member function,上述暂时对象被转换为一个cosnt char*,pc设置为data指针.
(3)暂时性String对象被销毁时,它的destructor被调用,它的data指针被删除,此时pc指向一块被删除过的地方.

8,总结:const member functions传回handles是不好的行为,甚至对non-const member functions而言,传回handles也会导致麻烦,特别是设计暂时性对象时.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值