menu

hjk41的日志

Avatar

Effective C++ Note 29: Avoid returning "handles" to internal data

What is wrong with the following code?

class String {
public:
  String(const char *value);        // see Item 11 for pos-
  ~String();                        // sible implementations;
                                    // see Item M5 for comments
                                    // on the first constructor
  operator char *() const;          // convert String -> char*;
                                    // see also Item M5
  ...

private:
  char *data;
};
const String B("Hello World");      // B is a const object

char *str = B;               // calls B.operator char*()
strcpy(str, "Hi Mom");       // modifies what str
                             // points to


Well, constant object B is modified. Why is that? That is because we returned a handle to the internal member variable data, and we made the member function operator *() const, which means that it can be called on a const object.
A better solution would be like this:

class String {
public:
  ...
  operator const char *() const;          // convert String -> char*;
  ...
};


Another operator of String object usually overloaded is operator[].

class String {
public:
  ...
  char& operator[](int index) const
  { return data[index]; }
private:
  char *data;
};
String s = "I'm not constant";
s[0] = 'x';               // fine, s isn't const
const String cs = "I'm constant";
cs[0] = 'x';              // this modifies the const
                          // string, but compilers
                          // won't notice


Here, constant Object cs is changed. What do we do, then? Perhaps the best way is to implement two different operator[].

class String {
public:
  ...
  char& operator[](int index)
  { return data[index]; }
  const char& operator[](int index) const
  { return data[index]; }
private:
  char *data;
};

However, there is still some problems with the code above. What if we do this:

String f1(){return "Hello World";}
    const char * str=f1;
    const char &ch=f1[0];
    cout<<str<<ch;


The result is undefined. Because the lifetime of temporary objects extends only until the end of the expression containing the call to the function. So when we call cout<<str<<ch the objects of which the data they point to have been destroyed. And using a pointer or a reference to unknown memory always lead to undefined behavior.

In a word, though it is rarely possible to eliminate all dangling pointers, it is better that we avoid returning handles to internal data.

评论已关闭