Назад | Начало урока | Вперед
Содержание

Джесс Либерти "С++ за 21 день"

День 13

Массивы и связные списки

Вверх

Массивы указателей
В этой книге рассматриваются массивы указателей, массивы,
нахождящиеся в динамической памяти, и другие типы структур.
Но более подробная информация по этой теме приведена в моей
предыдущей книгк "C++ Unleashed", опубликованной в издательстве
Sams Publishing.

До сих пор обсуждались массивы, члены которых размещались в стеке.
Но размер стека значительно меньше объема динамической памяти,
поэтому можно объявить объекты массива в динамической памяти,
а в самом массиве хранить лишь указатели на них. Это существенно
уменьшает объем используемой стековой памяти.

Листинг lib13_06.cpp демонстрирует это. Это видоизмененная программа
lib13_04.cpp .

Массив указателей на объекты



// Listing 13.6 - An array of pointers to objects

#include <iostream>
using namespace std;

class CAT
{

public:
CAT() {
itsAge = 1;
itsWeight=5;
}
~CAT() {} // destructor

int GetAge() const {

return itsAge;
}
int GetWeight() const {
return itsWeight;
}
void SetAge(int age) {
itsAge = age;
}

private:
int itsAge;
int itsWeight;

};

int main() {

CAT * Family[500];
int i;
CAT * pCat;
for (i = 0; i < 500; i++)
{
pCat = new CAT;
pCat->SetAge(2*i +1);
Family[i] = pCat;
}

for (i = 0; i < 500; i++)
{

cout << "Cat #" << i+1 << ": ";
cout << Family[i]->GetAge() << endl;
}
return 0;
}


Результат:

Cat #1: 1
Cat #2: 3
Cat #3: 5
Cat #4: 7
Cat #5: 9

. . .

Cat #494: 987
Cat #495: 989
Cat #496: 991
Cat #497: 993
Cat #498: 995
Cat #499: 997
Cat #500: 999

Анализ:

Класс CAT объявленный в строках 5-17 идентичен классу CAT, объявленному в
листинге 13.4 А массив Family объявленный в главной программе на сей раз
содержит 500 указателей на объекты класса CAT. (массив указателей на объекты
класса CAT).
CAT * Family[500];
Далее объявляется еще один отдельный указатель pCat на объект класса CAT.
Этот указатель потребуется для использования его внутри цикла.
Далее этот указатель pCat переопределяется на динамическую память,
то есть теперь указатель pCat указывает на место в динамической
памяти где будет содержаться объект типа CAT.
Пока в динамической памяти выделено место для объекта типа CAT.

(CAT * pCat;) //предыдущая инструкция объявлен указатель на объект
//класса CAT
pCat = new CAT; //этому указателю присвоен адрес в динамической памяти

Оператор new возвращает адрес выделенного места под объект в динамической
памяти, и этот адрес присваивается указателю pCat.

Затем в поле этого созданного объекта, который находится в области
динамической памяти внесем данные при помощи вызова функции SetAge()
с аргументом

pCat->SetAge(2*i +1);

В следующей инструкции передадим адрес выделенного места под объект в
динамической памяти, (в эту память уже внесены данные) элементу под
индексом i из массива указателей Family[].

Family[i] = pCat;

Теперь один из элементов массива указателей под индексом i указывает на
нами созданный объект в динамической памяти.

Далее цикл повторяется. И при каждой итерации цикла в динамической памяти
будет создаваться новый объект типа CAT, а затем адрес этого объекта будет
передаваться следующему элементу из массива указателей Family под индексом i.

Вспомним программу с атлетами из книги Франка , когда при каждой итерации
появлялся на экране новый атлет. Каждый раз на новом месте, так можно было
заполнить весь экран атлетами (смотри программа .....)

Здесь то же самое новые объекты типа CAT создаются при каждой итерации цикла,
и каждый новый объект занимает новое место, возможно рядом с предыдущим в
динамической памяти. Разумеется так можно заполнить и всю динамическую память.

Здесь массив указателей
CAT * Family[500];
создан в стеке, но при инициализации в каждый указатель этого массива вносится
адрес указывающий на динамическую память. По этому адресу в динамической
памяти уже находятся объекты с их данными.

Вверх

Объявление массивов в области динамической памяти

Весь массив можно разместить в области динамической памяти известной так же
под именем heap. Для этого при объявлении массива используется оператор new.
Результатом окажется указатель на пространство в динамической памяти,
где и будет содержаться массив. Пример:

CAT*Family=new CAT[500]

Здесь объявлено что Family будет указателем на первый элемент массива из 500
объектов класса CAT. Другими словами Family указывает на элемент (или содержит
адрес) Family[0].

Преимуществом такого способа использования массива является возможность
арифметических операций над указателями для доступа к каждому элементу
массива Family. Например:

CAT*Family=new CAT[500];
CAT*pCat=Family; //pCat указывает на Family[0]
pCat->SetAge[10];//присвоить Family[0] значение 10
pCat++; //вперед к Family[1]
pCat->SetAge[20];//присвоить Family[1] значение 20

Здесь в динамической памяти объявлен массив из 500 объектов класса CAT,
а так же создан указатель pCat, которому присваивается адрес первого элемента
массива или иными словами адрес начала массива.

CAT*pCat=Family; //pCat указывает на Family[0]

Теперь указатель pCat указывает на первый элемент массива CAT расположенный в
динамической памяти. Теперь при помощи функции с аргументами SetAge() в
поля этого элемента будут занесены данные:

pCat->SetAge[10];//присвоить Family[0] значение 10

Затем следует инструкция
pCat++; //вперед к Family[1]

Эта инструкция увеличивает указатель pCat на единицу, но указывает он теперь
уже на следующий элемент(!) массива типа CAT. Таковы свойства и возможности
языка С++.

Итак теперь указатель увеличивается и указывает уже на следующий объект
класса CAT, поэтому при вызове функции SetAge() значение 20 будет присвоено
второму объекту.

Вот как в свете изложенного модифицирована предыдущая программа:

Вверх


#include <iostream>
using namespace std;

class CAT
{

public:
CAT() {
itsAge = 1; itsWeight=5;
}
~CAT() {} // destructor
int GetAge() const {
return itsAge;
}
int GetWeight() const {
return itsWeight;
}
void SetAge(int age) {
itsAge = age;
}

private:
int itsAge;
int itsWeight;

};

int main() {

CAT*Family=new CAT[500];
CAT*pCat=Family; //pCat указывает на Family[0]
pCat->SetAge(10);//присвоить Family[0] значение 10

cout << pCat->GetAge() << endl;

pCat++; //вперед к Family[1]
pCat->SetAge(20);//присвоить Family[1] значение 20

cout << pCat->GetAge() << endl;
cout << Family[0].GetAge() << endl;
cout << Family[1].GetAge() << endl;

return 0;

}


Результат:

10
20
10
20

Анализ:

Вопрос:Почему здесь нельзя обойтись без промежуточного звена pCat ?
Потому, что к членам массива можно подойти либо через индекс, либо
через этот указатель. Например обратиться к 10-му элементу массива
Family[9].GetAge();
pCat+10->GetAge();
Чтобы перебрать 100 элементов этого массива можно увеличивать индекс
например так:

CAT*Family=new CAT[500];

int n=o;
for(int i=0;i<100;i++)
{

Family[n].GetAge();
n++;
}

А можно так:

CAT*Family=new CAT[500];
CAT*pCat=Family; //pCat указывает на Family[0]

for(int i=0;i<100;i++)
{

pCat->SetAge[10];//присвоить Family[0] значение 10
pCat++;
}

Вверх

Указатель на массив и массив указателей

Рассмотрим три объявления:
CAT FamilyOne[500];
CAT *FamilyTwo[500];
CAT *FamilyThree=new CAT[500];

CAT FamilyOne[500]; это массив из 500 объектов класса CAT,
CAT * FamilyTwo[500]; массив из 500 указателей на объекты класса CAT, а
CAT * FamilyThree = new CAT[500]; -указатель на массив из 500 объектов
класса CAT.

Различие между этими тремя строками весьма существенно и влияет на способ
существования массивов. Но самым удивительным оказывается то, что FamilyThree
является вариантом FamilyOne , а от FamilyTwo отличается принципиально.

В этом вся суть проблемы взаимосвязи указателей и массивов.
В третьем случае FamilyThree представляет собой указатель на массив. То есть
адрес находящийся в указателе FamilyThree , является адресом первого
элемента в этом массиве. Но это аналогично тому, что имеет место для FamilyOne!

Вверх

Имена массивов и указателей

В С++ имя массива является постоянным указателем на первый элемент массива.

CAT Family[50];

Следовательно здесь Family представляет собой указатель на переменную
&Family[0], являющуюся первым элементом массива Family.

Вполне допустимо использовать имена массивов как постоянные указатели,
и наоборот. Следовательно Family+4 -вполне законный способ доступа
к данным в элементе Family[4].

При инкременте и декременте указателя, компилятор делает все вычисления сам.
Адрес, полученный в результате вычисления выражения Family+4 , не на четыре
байта больше исходного, а на четыре объекта.
Следующий листинг демонстрирует объявление и использование массива
в динамической памяти.

Создание массива в динамической памяти


// Listing 13.7 - An array on the free store

#include <iostream>

class CAT
{

public:
CAT() {
itsAge = 1;
itsWeight=5;
}
~CAT();
int GetAge() const {
return itsAge;
}
int GetWeight() const {
return itsWeight;
}
void SetAge(int age) {
itsAge = age;
}

private:
int itsAge;
int itsWeight;

};

CAT :: ~CAT() {

// cout << "Destructor called!\n";
}

int main()
{

25: CAT * Family = new CAT[500];
int i;

for (i = 0; i < 500; i++)
{

Family[i].SetAge(2*i +1);
}

for (i = 0; i < 500; i++)
{

std::cout << "Cat #" << i+1 << ": ";
std::cout << Family[i].GetAge() << std::endl;
}

delete [] Family;

return 0;

}

Результат:

Cat #1: 1
Cat #2: 3
Cat #3: 5
Cat #4: 7
Cat #5: 9
...
Cat #496: 991
Cat #497: 993
Cat #498: 995
Cat #499: 997
Cat #500: 999

Анализ:

//В строке 25 объявлен массив Family содержащий 500 объектов класса CAT.
// Весь массив создан в динамической памяти с помощью выражения new CAT[500].
// CAT * Family = new CAT[500];
// в следующем цикле этот массив инициализирует свои данные-члены. Далее
// в следующем цикле этот массив выводится на экран
Вопрос:почему здесь не применяется оператор (->) при обращении к функциям
-членам?
Можно применить этот оператор если объявить еще один указатель на тип
CAT и присвоить ему адрес первого элемента массива Family.


int main()
{

25: CAT * Family = new CAT[500];
int i;
CAT *pFam = Family;
for (i = 0; i < 500; i++)
{
// Family[i].SetAge(2*i +1);
pFam->SetAge(2*i +1);
pFam++;
}

for (i = 0; i < 500; i++)
{

std::cout << "Cat #" << i+1 << ": ";
std::cout << Family[i].GetAge() << std::endl;
}

delete [] Family;

return 0;

}


Откомпилируй и сравни результаты обеих программ!

Результат:

Cat #1: 1
Cat #2: 3
Cat #3: 5
Cat #4: 7
Cat #5: 9
Cat #6: 11
Cat #7: 13
...
Cat #496: 991
Cat #497: 993
Cat #498: 995
Cat #499: 997
Cat #500: 999

Вверх

Удаление массивов из динамической памяти

Удаление массива Family автоматически возвращает всю выделенную для него
память, если оператор delete использован с квадратными скобками[]. Компилятор
достаточно вычисляет размер всех элементов массива и освобождает занимаемую
им область динамической памяти.

Для демонстрации этого в следующей программе уменьшим количество элементов
в массиве до 10 и раскомментируем строки внутри деструктора чтобы увидеть что
происходит:
По достижению строки 39 массив будет удален и для каждого объекта класса CAT
вызван деструктор.
--------------------------------------------------------------------------------
Когда с помощью оператора new в динамической памяти создается элемент,
то при его удалении непременно следует освободить занимаемую им область
динамической памяти.
Создав в динамической памяти массив с помощью оператора new[size],
впоследствии необходимо освободить эту область памяти с помощью оператора
delete[]. Квадратные скобки сообщают компилятору о том что удаляется массив.
Если квадратные скобки отсутствуют то будет освобожден лишь первый объект
массива. В этом легко убедиться убрав квадратные скобки в строке 39 программы
----------------------------------------------------------------------------------


// Listing 13.7 - An array on the free store

#include <iostream>

class CAT
{

public:
CAT() {
itsAge = 1;
itsWeight=5;
}
~CAT();
int GetAge() const {
return itsAge;
}
int GetWeight() const {
return itsWeight;
}
void SetAge(int age) {
itsAge = age;
}

private:
int itsAge;
int itsWeight;

};

CAT :: ~CAT() {

std:: cout << "Destructor called!\n";
}

int main()
{

CAT * Family = new CAT[10];
int i;

for (i = 0; i < 10; i++)
{

Family[i].SetAge(2*i +1);
}

for (i = 0; i < 10; i++)
{

std::cout << "Cat #" << i+1 << ": ";
std::cout << Family[i].GetAge() << std::endl;
}

39: delete [] Family;

return 0;

}


Результат:

Cat #1: 1
Cat #2: 3
Cat #3: 5
Cat #4: 7
Cat #5: 9
Cat #6: 11
Cat #7: 13
Cat #8: 15
Cat #9: 17
Cat #10: 19
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!
Destructor called!

Анализ:

----------------------------------------------------------------------------------
Применяйте для доступа к элементам массива индексы, а для доступа к указателям
массива точечный оператор.
----------------------------------------------------------------------------------
Не записывайте и не читайте данных вне пределов массива.
Не путайте массив указателей с указателем на массив.
----------------------------------------------------------------------------------

Вверх

Массивы символов

Можно создавать и неинициализированные символьные массивы , но при этом надо
следить чтобы в этот буфер было записано данных не более чем он может
вместить.
Посмотрите программу, которая демонстрирует использование неинициализированного
буфера:


//Listing 13.8 char array buffers

#include <iostream>

int main()
{

char buffer[80];
std::cout << "Enter the string: ";
std::cin >> buffer;
std::cout << "Here is's the buffer: " << buffer << std::endl;
return 0;
}

Результат:

Enter the string:Hello World
Here is's the buffer:Hello

Анализ:

Здесь пользователю предлагают ввести строку зарезервировано место под 80
символов. Но здесь возникают две проблемы:

Во первых если пользователь вводит строку длинее 80 символв, то оператор cin
осуществит запись за пределы буфера.

Во вторых если пользователь введет пробел, то cin воспримет его как конец
строки и становит запись в буфер.

Для разрешения этих проблем нужно создать специальную функцию cin.get()
принимающую три параметра

-буфер для заполнения
-максимальное число символов
-символ для завершения ввода

По умолчанию критерием завершения ввода является символ новой строки
Посмотрите программу:

Вверх


//Listing 13.9 using cin.get()

#include <iostream>
using namespace std;

int main()
{

char buffer[80];
cout << "Enter the string: ";
9: cin.get(buffer, 79); // get up to 79 or newline
cout << "Here's the buffer: " << buffer << endl;
return 0;
}

Результат:

Enter the string:Hello World
Here is's the buffer:Hello World

Анализ:

В строке 9 вызывается функция cin.get() Буфер, объявленный в строке 7
передается в качестве первого аргумента. Второй аргумент-максимальное
число вводимых символов -79 чтобы учесть завершающий символ null.
Третий параметр необязателен, поскольку по умолчанию признаком завершения
является новая строка.

Вверх

Функции strcpy() strncpy()

Посмотрите программу, демонстрирующую функцию strcpy()


//Listing 13.10 Using strcpy()

#include <iostream>
#include <string.h>
using namespace std;

int main()
{

char String1[] = "No man is an island";
char String2[80];

strcpy(String2, String1);

cout << "String1: " << String1 << endl;
cout << "String2: " << String2 << endl;
return 0;

}


Результат:

Результат:
String1 = "No man is an island
String2 = "No man is an island

Анализ:

Файл заголовка string.h подключен в строке 3 Этот файл содержит прототип
функции strcpy() принимающей два символьных массива:результирующий и
исходный.
Если исходный массив окажетсяя больше результирующего, то strcpy() осуществит
запись за пределы результирующего буфера.
Чтобы избежать этого, стандартная библиотека располагает функцией strncpy()
Этот вариант принимает в качестве третьего аргумента и максимальное
число копируемых символов.
strncpy() осуществляет копирование до первого символа null или максимального
числа символов, определенного для результирующего буфера.

Посмотрите программу где используется функция strncpy() :


//Listing 13.11 Using strncpy()

#include <iostream>
#include <string.h>

int main()
{

const int MaxLength = 80;
char String1[] = "No man is an island";
char String2[MaxLength+1];

strncpy(String2, String1, MaxLength);

std::cout << "String1: " << String1 << std::endl;
std::cout << "String2: " << String2 << std::endl;
return 0;

}


Результат:

String1 = "No man is an island
String2 = "No man is an island

Анализ:
Буфер String2 объявлен как массив из MaxLength+1 символов. Дополнительный
элемент предназначен для символа null который и strcpy() и strncpy()
добавляют в конец строки автоматически.

Вверх

Строковые классы

В качестве упражнения создадим собственный специальный класс String.
Этот класс должен преодолеть исходные ограничения символьных массивов

Подобно всем остальным массивам, символьные массивы статичны.
Их размер задается при объявлении и независимо от того, какое
количество элементов массива используется, размер занимаемого
участка памяти остается постоянным, и запись за пределы массива
чревата неприятностями.


//Listing 13.12 Using a String class

#include <iostream>
#include <string.h>
using namespace std;

// Rudimentary string class
class String
{

public:
// constructors
String();
String(const char *const);
String(const String &);
~String();

// overloaded operators
char & operator[](unsigned short offset);
char operator[](unsigned short offset) const;
String operator+(const String&);
void operator+=(const String&);
String & operator= (const String &);

// General accessors
unsigned short GetLen()const {

return itsLen;
}
const char * GetString() const {
return itsString;
}

private: 28: String (unsigned short); // private constructor
char * itsString;
unsigned short itsLen;

};

// default constructor creates string of 0 bytes
String::String()
{

itsString = new char[1];
itsString[0] = '\0';
itsLen=0;
}

// private (helper) constructor, used only by
// class methods for creating a new string of
// required size. Null filled.
String::String(unsigned short len)
{

itsString = new char[len+1];
for (unsigned short i = 0; i<=len; i++)
itsString[i] = '\0';
itsLen=len;
}

// Converts a character array to a String
String::String(const char * const cString)
{

itsLen = strlen(cString);
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = cString[i];
itsString[itsLen]='\0';
}

63:// copy constructor
String::String (const String & rhs)
{

itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = rhs[i];
itsString[itsLen] = '\0';
}

// destructor, frees allocated memory
String::~String ()
{

delete [] itsString;
itsLen = 0;
}

// operator equals, frees existing memory
// then copies string and size
String& String::operator=(const String & rhs)
{

if (this == &rhs)
return *this;
delete [] itsString;
itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = rhs[i];
itsString[itsLen] = '\0';
return *this;
}

//nonconstant offset operator, returns
// reference to character so it can be
// changed!
char & String::operator[](unsigned short offset)
{

if (offset > itsLen)
return itsString[itsLen-1];
else
return itsString[offset];
}

// constant offset operator for use
// on const objects (see copy constructor!)
char String::operator[](unsigned short offset) const
{

if (offset > itsLen)
return itsString[itsLen-1];
else
return itsString[offset];
}

// creates a new string by adding current
// string to rhs
String String::operator+(const String& rhs)
{

unsigned short totalLen = itsLen + rhs.GetLen();
String temp(totalLen);
unsigned short i;
for ( i= 0; i temp[i] = itsString[i];
for (unsigned short j = 0; j temp[i] = rhs[j];
temp[totalLen]='\0';
return temp;
}

// changes current string, returns nothing
void String::operator+=(const String& rhs)
{

unsigned short rhsLen = rhs.GetLen();
unsigned short totalLen = itsLen + rhsLen;
String temp(totalLen);
unsigned short i;
for (i = 0; i temp[i] = itsString[i];
for (unsigned short j = 0; j temp[i] = rhs[i-itsLen];
temp[totalLen]='\0';
*this = temp;
}

int main()
{

String s1("initial test");
cout << "S1:\t" << s1.GetString() << endl;

char * temp = "Hello World";
s1 = temp;
cout << "S1:\t" << s1.GetString() << endl;

char tempTwo[20];
strcpy(tempTwo, "; nice to be here!");
s1 += tempTwo;
cout << "tempTwo:\t" << tempTwo << endl;
cout << "S1:\t" << s1.GetString() << endl;

cout << "S1[4]:\t" << s1[4] << endl;
s1[4]='x';
cout << "S1:\t" << s1.GetString() << endl;

cout << "S1[999]:\t" << s1[999] << endl;

String s2(" Another string");
String s3;
s3 = s1+s2;
cout << "S3:\t" << s3.GetString() << endl;

String s4;
s4 = "Why does this work?";
cout << "S4:\t" << s4.GetString() << endl;
return 0;

}


Результат:

S1: initial test
S1: Hello World
tempTwo: ; nice to be here!
S1: Hello World; nice to be here!
S1[4]: o
S1: Hellx World; nice to be here!
S1[999]: !
S3: Hellx World; nice to be here! Another string
S4: Why does this work?

Анализ:

Мы видим в программе объявление класса String.
Строки 11-13 содержат объявления трех конструкторов:
стандартного,
конструктора копий,
конструктора, принимающего строку стиля С, которая завершается символом null.

// constructors
String();
String(const char *const);
String(const String &);

Далее класс String перегружает оператор индекса [], оператор плюс (+),
и оператор плюс-эквалс (+=) (присвоение с суммой)
// overloaded operators
char & operator[](unsigned short offset);
char operator[](unsigned short offset) const;
String operator+(const String&);
void operator+=(const String&);
String & operator= (const String &);

Как видим оператор индекса перегружен дважды.
Один раз как постоянная функция, возвращающая значение типа char,

char operator[](unsigned short offset) const;

а второй раз как не постоянная функция, возвращающая ссылку на значение
типа char.

char & operator[](unsigned short offset);

Не постоянная версия используется в операторах типа (строка 161):

SomeString[4]='x';

Это обеспечивает прямой доступ к любому символу строки.Таким образом
получив ссылку на символ и вызвав эту функцию, можно изменить его значение.

cout << "S1[4]:\t" << s1[4] << endl;
s1[4]='x';
cout << "S1:\t" << s1.GetString() << endl;
Эти строки из главной программы изменяют значение символа под индексом 4
в символьном массиве s1.

Постоянная версия оператора используется в тех случаях, когда необходимо
получить доступ к постоянному объекту класса String , например в реализации
конструктора копий (строка 63)

63:// copy constructor
String::String (const String & rhs)
{

itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = rhs[i];
itsString[itsLen] = '\0';
}

Обратите внимание что rhs[i] доступен, хотя rhs был объявлен как const String &.
К этому объекту не возможно получить доступ, используя непостоянные функции-члены.
Следовательно оператор индекса необходимо перегружать как постоянный.

Если возвращаемый объект окажется слишком большим, то придется вернуть не сам объект,
а постоянную ссылку на него. Но поскольку один символ занимает только один байт,
то нет смысла так поступать.

Стандартный конструктор реализован в строках 31-39

Стандартный конструктор создает строку нулевой длины

// default constructor creates string of 0 bytes

String::String()
{

itsString = new char[1];
itsString[0] = '\0';
itsLen=0;
}

Общепринято что в классе String длина строки измеряется без учета концевого
символа null. Таким образом строка созданная по умолчанию содержит лишь
концевой символ null.

Конструктор копий реализован в строках 63-70

63:// copy constructor
String::String (const String & rhs)
{

itsLen=rhs.GetLen();
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = rhs[i];
itsString[itsLen] = '\0';
}

Он устанавливает длину новой строки на единицу больше длины исходной
(дополнительный символ необходим для завершающейго null), а затем копирует
каждый символ исходной строки во вновь созданную и завершает ее символом null.

В строках 53-60 реализован конструктор, принимающий исходную строку в стиле С
(с концевым null)Этот конструктор подобен конструктору копий. Длина исходной
строки исчисляется с помощью функции strlen() из стандартной библиотеки String.

Конструктор преобразует символьный массив в строку

// Converts a character array to a String
String::String(const char * const cString)
{

itsLen = strlen(cString);
itsString = new char[itsLen+1];
for (unsigned short i = 0; i itsString[i] = cString[i];
itsString[itsLen]='\0';
}

В строке 28 объявлен еще один конструкто, String (unsigned short)

String (unsigned short); // private constructor

Он объявлен как закрытая функция-член. Таково было намерение автора,
чтобы ни один из клиентов этого класс не мог создать строку произвольной
длины. Этот конструктор создает строки для внутреннего пользования,
как например operator += в строке 130. Более подробная информация об операторе
operator += приведена далее в этой главе.

Конструктор String (unsigned short); // private constructor
заполняет все элементы строки символом null.
Поэтому условием выхода из цикла for будет i // private (helper) constructor, used only by
// class methods for creating a new string of
// required size. Null filled.

String::String(unsigned short len)
{

itsString = new char[len+1];
for (unsigned short i = 0; i<=len; i++)
itsString[i] = '\0';
itsLen=len;
}


Назад | Начало урока | Вверх | Вперед
Содержание

Hosted by uCoz