Анимация
JavaScript


Главная  Библионтека 

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

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