Рейтинговые книги
Читем онлайн Освой самостоятельно С++ за 21 день. - Джесс Либерти

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 101 102 103 104 105 106 107 108 109 ... 170

Примечание:От начинающих программистов C++ часто можно услышать замечание, что объявление классов-друзей противоречит принципу инкапсуляции, лежащему в основе объектно-ориентированного программирования. Это, честно говоря, довольно широко распространенная бессмыслица. Объявление класса-друга просто расширяет интерфейс другого класса, что влияет на инкапсуляцию не больше, чем открытое наследование классов.

Дружественный класс

Объявление одного класса другом какого-либо иного с помощью ключевого слова friend в объявлении второго класса открывает первому классу доступ к членам второго класса. Иными словами, я могу объявить вас своим другом, но вы не можете объявить себя моим другом. Пример:

class PartNode{

   public:

   friend class PartsList: // обьявление класса PartsList другом PartNode

Функции друзья

Иногда бывает необходимо предоставить права доступа не всему классу, а только одной или нескольким функциям-членам. Это реализуется посредством объявления друзьями функций-членов другого класса. Причем объявлять другом весь класс вовсе не обязательно. Фактически другом можно объявить любую функцию, независимо от того, является ли она функцией-членом другого класса или нет.

Функции друзья и перегрузка оператора

В листинге 15.1 представлен класс String, в котором перегружается operator+. В нем также объявляется конструктор, принимающий указатель на константную строку, поэтому объект класса String можно создавать из строки с концевым нулевым символом.

Примечание:Строки в С и C++ представляют собой массивы символов, заканчивающиеся концевым нулевым символом. Такая строка получается, например,в следующем выражении присвоения: myString[] = "Hello World".

Но чего невозможно сделать в классе String, так это получить новую строку в результате сложения объекта этого класса с массивом символов:

char cString[] = { "Hello"} ; String sString(" Worid");

String sStringTwo = cString + sString; //ошибка!

Строки нельзя использовать с перегруженной функции operator+. Как объяснялось на занятии 10, выражение cString + sString на самом деле вызывает функцию cString.operator+(sString). Поскольку функция operator+() не может вызываться для символьной строки, данная попытка приведет к ошибке компиляции.

Эту проблему можно решить, объявив функцию-друга в классе String, которая перегружает operator+ таким образом, чтобы суммировать два объекта String. Соответствующий конструктор класса String преобразует строки в объекты String, после чего вызывается функция-друг operator+, выполняющая конкатенацию двух объектов.

Листинг 15.8. Функция-друг operator+

1: // Листинг 15.8. Операторы друзья

2:

3: #include <iostream.h>

4: #include <string.h>

5:

6: // Рудиментарный класс string

7: class String

8: {

9:    public:

10:      // constructors

11:      String();

12:      String(const char *const);

13:      String(const String &);

14:      ~String();

15:

16:      // перегруженные операторы

17:     char & operator[](int offset);

18:     char operator[](int offset) const;

19:     String operator+(const String&);

20:     friend String operator+(const String&, const String&);

21:     void operator+=(const String&);

22:     String & operator= (const String &);

23:

24:     // методы общего доступа

25:     int GetLen()const { return itsLen; }

26:     const char * GetString() const { return itsString; }

27:

28:  private:

29:     String (int); // закрытый конструктор

30:     char * itsString;

31:     unsigned short itsLen;

32: };

33:

34: // конструктор, заданный по умолчанию, создает строку длиной 0 байт

35: String::String()

36: {

37:    itsString = new char[1];

38:    itsString[0] = '';

39:    itsLen=0;

40:    // cout << "tDefault string constructorn";

41:    // ConstructorCount++:

42: }

43:

44: // закрытый конструктор, используемый только

45: // методами класса для создания новой строки

46: // указанного размера, заполненной нулями.

47: String::String(int len)

48: {

49:    itsString = new char[len+1];

50:    for (int i = 0; i<=len; i++)

51:       itsString[i] = '';

52:    itsLen=len;

53:    // cout << "tString(int) constructorn";

54:    // ConstructorCount++;

55: }

56:

57: // Преобразует массив символов в строку

58: String::String(const char * const cString)

59: {

60:    itsLen = strlen(cString);

61:    itsString = new char[itsLen+1];

62:    for (int i = 0; i<itsLen; i++)

63:       itsString[i] = cString[i];

64:    itsString[itsLen]='';

65:    // cout << "tString(char*) constructorn";

66:    // ConstructorCount++;

67: }

68:

69: // конструктор-копировщик

70: String::String (const String & rhs)

71: {

72:    itsLen=rhs.GetLen();

73:    itsString = new char[itsLen+1];

74:    for (int i = 0; i<itsLen;i++)

75:       itsString[i] = rhs[i];

76:    itsString[itsLen] = '';

77:    // cout << "tString(String&) constructorn";

78:    // ConstructorCount++;

79: }

80:

81: // деструктор, освобождает занятую память

82: String::~String ()

83: {

84:    delete [] itsString;

85:    itsLen = 0;

86:    // cout << "tString destructorn";

87: }

88:

89: // этот оператор освобождает память, а затем

90: // копирует строку и размер

91: String& String::operator=(const String & rhs)

92: {

93:    if (this == &rhs)

94:    return <<this;

95:    delete [] itsString;

96:    itsLen=rhs.GetLen();

97:    itsString = new char[itsLen+1];

98:    for (int i = 0; i<itsLen;i++)

99:    itsString[i] = rhs[i];

100:   itsString[itsLen] = 1';

101:   return *this;

102:   // cout << "tString operator=n";

103: }

104:

105: // неконстантный оператор индексирования,

106: // возвращает ссылку на символ, который можно

107: // изменить!

108: char & String::operator[](int offset)

109: {

110:    if (offset > itsLen)

111:       return itsString[itsLen-1];

112:    else

113:       return itsString[offset];

114: }

115:

116: // константный оператор индексирования,

117: // используется для константных объектов (см. конструктор-копировщик!)

118: char String::operator[](int offset) const

119: {

120:    if (offset > itsLen)

121:       return itsString[itsLen-1];

122:    else

123:       return itsString[offset];

124: }

125: // создает новый объект String, добавляя

126: // текущий обьект к rhs

127: String String::operator+(const String& rhs)

128: {

129:    int totalLen = itsLen + rhs.GetLen();

130:    String temp(totalLen);

131:    int i, j;

132:    for (i = 0; i<itsLen; i++)

133:       temp[i] = itsString[i];

134:    for (j = 0, i = itsLen; j<rhs.GetLen(); j++, i++)

135:    temp[i] = rhs[j];

136:    temp[totalLen]='';

137:    return temp;

138: }

139:

140: // создает новый объект String

141: // из двух объектов класса String

142: String operator+(const String& lhs, const String& rhs)

143: {

144:    int totalLen = lhs.GetLen() + rhs.GetLen();

145:    String temp(totalLen);

146:    int i, j;

147:    for (i = 0; i<lhs.GetLen(); i++)

148:       temp[i] = lhs[i];

149:    for (j = 0, i = lhs.GetLen();; j<rhs.GetLen(); j++, i++)

150:       temp[i] = rhs[j];

151:    temp[totalLen]='';

152:    return temp;

153: }

154:

155: int main()

156: {

157:    String s1("String 0ne ");

158:    String s2("String Two ");

159:    char *c1 = { "C-String 0ne " } ;

160:    String s3;

161:    Stnng s4;

162:    String s5;

163:

164:    cout << "s1: " << s1.GetString() << endl;

165:    cout << "s2: " << s2.GetString() << endl;

166:    cout << "c1: " << c1 << endl;

167:    s3 = s1 + s2;

168:    cout << "s3: " << s3.GetString() << endl;

169:    s4 = s1 + cl;

170:    cout << "s4: " << s4.GetStnng() << endl;

171:    s5 = c1 + s2;

172:    cout << "s5: " << s5.GetString() << endl;

173:    return 0;

174: }

Результат:

s1: String 0ne

s2: String Two

c1: C-String One

s3: String One String Two

s4: String One C-String One

s5: C-String One String Two

Анализ: Объявления всех методов класса String, за исключением operator+, остались такими же, как в листинге 15.1. В строке 20 листинга 15.8 перегружается новый operator+, который принимает две ссылки на константные строки и возвращает строку, полученную в результате конкатенации исходных строк. Эта функция объявлена как друг класса String.

Обратите внимание, что функция operator+ не является функцией-членом этого или любого другого класса. Она объявляется среди функций-членов класса string как друг, но не как член класса. Тем не менее это все же полноценное объявление функции, и нет необходимости еще раз объявлять в программе прототип этой функции.

Выполнение функции operator+ определяется в строках 142—153. Определение выполнения функции аналогично приведенному в версии программы, представленной в листинге 15.1, за тем исключением что функция принимает в качестве аргументов две строки, обращаясь к ними с помощью открытых методов доступа класса.

Перегруженный оператор применяется в строке 171, где выполняется конкатенация двух строк.

Функции-друзья

Для объявления функции как друга класса используется ключевое слово friend, за которым следует объявление функции Это не предоставляет функции доступ к указателю this, но обеспечивает доступ ко всем закрытым и защищенным данным и функциям-членам.

1 ... 101 102 103 104 105 106 107 108 109 ... 170
На этой странице вы можете бесплатно читать книгу Освой самостоятельно С++ за 21 день. - Джесс Либерти бесплатно.

Оставить комментарий