`
kmplayer
  • 浏览: 497061 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

条款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所指目标之物"的权利.
如下图所示:



实例代码:
#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也会导致麻烦,特别是设计暂时性对象时.
  • 大小: 24 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics