Анимация
JavaScript
|
Главная Библионтека string s; char array[12 8]; s.buf = array; и организация памяти разрушается, когда эта строка покидает область действия. Простое закрытие при помощи модификатора private поля buf не помогает, если вы продолжаете обеспечивать доступ посредством функции. Листинг 7 показывает фрагмент простого определения строки, которое будет использоваться мной несколько раз в оставшейся части этой главы. (Упрощение, сделанное мной, свелось к помещению всего в один листинг; обычно определение класса и встроенные функции будут в заголовочном файле, а остальной код - в файле .cpp). Листинг 7. Простой строковый класс 1 class string 2{ 3 char *buf; 4 int length; длина буфера (не строки); 5 6 public: 7 virtual 8 ~string( void ); 9 string( const char *input str = ); 10 string( const string &r ); 12 virtual const string &operator=( const string &r ); 14 virtual int operator<( const string &r ) const; 15 virtual int operator>( const string &r ) const; 16 virtual int operator==( const string &r ) const; 18 virtual void print( ostream &output ) const; 19 20 }; 21 ----------------------------------------------------------------22 inline string::string( const char *input str /*= ""*/ ) 23 { 24 length = strlen(input str) + 1; 25 buf = new char[ length ]; 26 strcpy( buf, input str ); 27 } 28 ----------------------------------------------------------------29 inline string::string( const string &r ) 30 { 31 length = r.length; 32 buf = new char[ length ]; 33 strcpy( buf, r.buf ); 34 } 35 ----------------------------------------------------------------36 /* виртуальныЙ */ string:: ~string( void ) 38 delete buf; 39 } 40 ----------------------------------------------------------------41 /* виртуальный */ const string &string::operator=( const string &r) 42 { 43 if( this != &r ) 44 { 45 if( length != r.length ) 46 { 4 7 free( buf ); 48 length = r.length; 49 buf = new char [ length ]; 50 } 51 strcpy( buf, r.buf ); 52 } 53 return *this; 54 } 56 ----------------------------------------------------------------57 /* виртуальный */ int string::operator<( const string &r ) const 58 { 59 return strcmp(buf, r.buf) < 0; 60 } 61 ----------------------------------------------------------------62 /* виртуальный */int string::operator>( const string &r ) const 63 { 64 return strcmp(buf, r.buf) > 0; 65 } 66 ----------------------------------------------------------------67 /* виртуальный */ int string::operator==( const string &r ) const 68 { 69 return strcmp(buf, r.buf) == 0; 70 } 71 ----------------------------------------------------------------72 /* виртуальный */ void string::print( ostream &output ) const 73 { 74 cout << buf; 75 } 76 ----------------------------------------------------------------77 inline ostream &operator<<( ostream &output, const string &s ) 78 { 79 Эта функция не является функцией-членом класса string, 80 но не должна быть дружественной, потому что мной тут 81 реализован метод вывода строкой своего значения. 83 s.print(output); 84 return output; 85 } Вы заметите, что я умышленно не реализовал следующую функцию в листинге 7: string:: operator const char*() { return buf; } Если бы реализовал, то мог бы сделать следующее: void f( void ) string s; ... printf("%s\n", (const char*)s ); но я не смогу реализовать функцию operator char*(), которая бы работала со строкой Unicode, использующей для символа 16-бит. Я должен бы был написать функцию operator wchar t*(), тем самым модифицировав код в функции f() : printf("%s/n", (const wchar t*)s ); Тем не менее, одним из главных случаев, которых я стараюсь избежать при помощи объектно-ориентированного подхода, является необходимость модификации пользователя объекта при изменении внутреннего определения этого объекта, поэтому преобразование в char* неприемлемо. Также есть проблемы со стороны внутренней согласованности. Имея указатель на buf, возвращенный функцией operator const char*() , вы все же можете модифицировать строку при помощи указателя и испортить поле length, хотя для этого вам придется немного постараться: string s; ... char *p=(char *)(const char *)s; gets( p ); В равной степени серьезная, но труднее обнаруживаемая проблема возникает в следующем коде: const char *g( void ) string s; ... return (const char *)s; Операция приведения вызывает функцию operator const char*() , возвращающую buf. Тем не менее, деструктор класса string передает этот буфер оператору delete, когда строка покидает область действия. Следовательно, функция g() возвращает указатель на освобожденную память. В отличие от предыдущего примера, при этой второй проблеме нет закрученного оператора приведения в два этапа, намекающего нам, 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 [ 48 ] 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |