НА ГЛАВНУЮ
Меню сайта
Категория
Ghost++ [1]
С++ [55]
Развлечение
ON - LINE
Опрос
Оцените мой сайт
Всего ответов: 199
Оbserver Ward

Онлайн всего: 1
Гостей: 1
Пользователей: 0


Друзья сайта
Заведи себе Бота
Hаша кнопка
Для обмена банерами , наша кнопка для размещения у вас на сайте

Клансайт USSR


Главная » Статьи » Программирование » С++

8. Область видимости и время жизни (2)
8.4.3. Динамическое создание и уничтожение массивов

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

// создание единственного объекта типа int
// с начальным значением 1024
int *pi = new int( 1024 );

// создание массива из 1024 элементов
// элементы не инициализируются
int *pia = new int[ 1024 ];

// создание двумерного массива из 4x1024 элементов
int (*pia2)[ 1024 ] = new int[ 4 ][ 1024 ];

pi содержит адрес единственного элемента типа int, инициализированного значением 1024; pia – адрес первого элемента массива из 1024 элементов; pia2 – адрес начала массива, содержащего четыре массива по 1024 элемента, т.е. pia2 адресует 4096 элементов.
В общем случае массив, размещаемый в хипе, не может быть инициализирован. (В разделе 15.8 мы покажем, как с помощью конструктора по умолчанию присвоить начальное значение динамическому массиву объектов типа класса.) Задавать инициализатор при выделении оператором new памяти под массив не разрешается. Массиву элементов встроенного типа, размещенному в хипе, начальные значения присваиваются с помощью цикла for:

for (int index = 0; index < 1024; ++index )
   pia[ index ] = 0;

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

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

const char *noerr = "success";
// ...
const char *err189 = "Error: a function declaration must "
             "specify a function return type!";

Размер создаваемого с помощью оператора new массива может быть задан значением, вычисляемым во время выполнения:

#include <cstring>
const char *errorTxt;
if (errorFound)
  errorTxt = errl89;
else
  errorTxt = noerr;
int dimension = strlen( errorTxt ) + 1;
char *strl = new char[ dimension ];
// копируем текст ошибки в strl
strcpy( strl, errorTxt );
dimension разрешается заменить выражением:
// обычная для С++ идиома,
// иногда удивляющая начинающих программистов
char *strl = new char[ str1en( errorTxt ) + 1 ];

Единица, прибавляемая к значению, которое возвращает strlen(), необходима для учета завершающего нулевого символа в C-строке. Отсутствие этой единицы – весьма распространенная ошибка, которую достаточно трудно обнаружить, поскольку она проявляет себя косвенно: происходит затирание какой-либо другой области программы. Почему? Большинство функций, которые обрабатывают массивы, представляющие собой С-строки символов, пробегают по элементам, пока не встретят завершающий нуль.
Если в конце строки нуля нет, то возможно чтение или запись в случайную область памяти. Избежать подобных проблем позволяет класс string из стандартной библиотеки С++.
Отметим, что только первое измерение массива, создаваемого с помощью оператора new, может быть задано значением, вычисляемым во время выполнения. Остальные измерения должны задаваться константами, известными во время компиляции. Например:

int getDim();
// создание двумерного массива
int (*pia3)[ 1024 ] = new int[ getDim() ][ 1024 ]; // правильно

// ошибка: второе измерение задано не константой
int **pia4 = new int[ 4 ][ getDim() ];

Оператор delete для уничтожения массива имеет следующую форму:

delete[] str1;

Пустые квадратные скобки необходимы. Они говорят компилятору, что указатель адресует массив, а не единичный элемент. Поскольку тип str1 – указатель на char, без этих скобок компилятор не поймет, что удалять следует целый массив.
Отсутствие скобок не является синтаксической ошибкой, но правильность выполнения программы не гарантируется (это особенно справедливо для массивов, которые содержат объекты классов, имеющих деструкторы, как это будет показано в разделе 14.4).
Чтобы избежать проблем, связанных с управлением динамически выделяемой памятью для массивов, рекомендуется пользоваться контейнерными типами из стандартной библиотеки, такими, как vector, list или string. Они управляют памятью автоматически. (Тип string был представлен в разделе 3.4, тип vector – в разделе 3.10. Подробное описание контейнерных типов см. в главе 6.)
8.4.4. Динамическое создание и уничтожение константных объектов

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

const int *pci = new const int(1024);

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

Во-вторых, указатель, возвращаемый выражением new, должен адресовать константу. В предыдущем примере pci служит указателем на const int.
Константность динамически созданного объекта подразумевает, что значение, полученное при инициализации, в дальнейшем не может быть изменено. Но поскольку объект динамический, временем его жизни управляет оператор delete. Например:

delete pci;

Хотя операнд оператора delete имеет тип указателя на const int, эта инструкция является корректной и освобождает область памяти, на которую ссылается pci.
Невозможно создать динамический массив константных элементов встроенного типа потому, что, как мы отмечали выше, элементы такого массива нельзя проинициализировать в операторе new. Следующая инструкция приводит к ошибке компиляции:

const int *pci = new const int[100]; // ошибка

8.4.5. Оператор размещения new А

Существует третья форма оператора new, которая создает объект без отведения для него памяти, то есть в памяти, которая уже была выделена. Эту форму называют оператором размещения new. Программист указывает адрес области памяти, в которой размещается объект:

new (place_address) type-specifier

place_address должен быть указателем. Такая форма (она включается заголовочным файлом <new>) позволяет программисту предварительно выделить большую область памяти, которая впоследствии будет содержать различные объекты. Например:

#include <iostream>
#include <new>
const int chunk = 16;
class Foo {
public:
    int val() { return _val; }
    FooQ(){ _val = 0; }
private:
    int _val;
};
// выделяем память, но не создаем объектов Foo
char *buf = new char[ sizeof(Foo) * chunk ];
int main() {
    // создаем объект Foo в buf
    Foo *pb = new (buf) Foo;
    // проверим, что объект помещен в buf
    if ( pb.val() == 0 )
        cout << "Оператор new сработал!" << endl;
    // здесь нельзя использовать pb
    delete[] buf;
    return 0;
}

Результат работы программы:

Оператор new сработал!

Для оператора размещения new нет парного оператора delete: он не нужен, поскольку эта форма не выделяет память. В предыдущем примере необходимо освободить память, адресуемую указателем buf, а не pb. Это происходит в конце программы, когда буфер больше не нужен. Поскольку buf ссылается на символьный массив, оператор delete имеет форму

delete[] buf;

При уничтожении buf прекращают существование все объекты, созданные в нем. В нашем примере pb больше не ссылается на существующий объект класса Foo.
Упражнение 8.5

Объясните, почему приведенные операторы new ошибочны:
(a) const float *pf = new const float[100];
(b) double *pd = new doub1e[10] [getDim()];
(c) int (*pia2)[ 1024 ] = new int[ ][ 1024 ];
(d) const int *pci = new const int;
Упражнение 8.6

Как бы вы уничтожили pa?
typedef int arr[10];
int *pa = new arr;
Упражнение 8.7

Какие из следующих операторов delete содержат потенциальные ошибки времени выполнения и почему:

int globalObj;
char buf[1000];
void f() {
  int *pi = &global0bj;
  double *pd = 0;
  float *pf = new float(O);
  int *pa = new(buf)int[20];
  delete pi; // (a)
  delete pd; // (b)
  delete pf; // (c)
  de1ete[] pa; // (d)
}

Упражнение 8.8

Какие из данных объявлений auto_ptr неверны или грозят ошибками времени выполнения? Объясните каждый случай.

int ix = 1024;
int *pi = & ix;
int *pi2 = new int ( 2048 );

(a) auto_ptr<int> p0(ix);
(b) auto_ptr<int> pl(pi);
(c) auto_ptr<int> p2(pi2);
(d) auto_ptr<int> p3(&ix);
(e) auto_ptr<int> p4(new int(2048));
(f) auto_ptr<int> p5(p2.get());
(9) auto_ptr<int> p6(p2.release());
(h) auto_ptr<int> p7(p2);

Упражнение 8.9

Объясните разницу между следующими инструкциями:
int *pi0 = p2.get();
int *pi1 = p2.release() ;
Для каких случаев более приемлем тот или иной вызов?
Упражнение 8.10

Пусть мы имеем:

auto_ptr< string > ps( new string( "Daniel" ) );

В чем разница между этими двумя вызовами assign()?Какой их них предпочтительнее и почему?

ps.get()->assign( "Danny" );
ps->assign( "Danny" );

8.5. Определения пространства имен А

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

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

Обойти эту проблему, названную проблемой засорения области видимости глобального пространства имен, можно посредством очень длинных имен. Часто в качестве их префикса употребляется определенная последовательность символов. Например:

class cplusplus_primer_matrix { ... };
void inverse( cplusplus_primer_matrix & );

Однако у этого решения есть недостаток. Программа, написанная на С++, может содержать множество глобальных классов, функций и шаблонов, видимых в любой точке кода. Работать со слишком длинными идентификаторами для программистов утомительно.
Пространства имен помогают справиться с проблемой засорения более удобным способом. Автор библиотеки может задать собственное пространство и таким образом вынести используемые в библиотеке имена из глобальной области видимости:

namespace cplusplus_primer {
   class matrix { /*...*/ };
   void inverse ( matrix & );
}

cplusplus_primer является пользовательским пространством имен (в отличие от глобального пространства, которое неявно подразумевается и существует в любой программе).
Каждое такое пространство представляет собой отдельную область видимости. Оно может содержать вложенные определения пространств имен, а также объявления или определения функций, объектов, шаблонов и типов. Все сущности, объявленные внутри некоторого пространства имен, называются его членами. Каждое имя в пользовательском пространстве, как и в глобальном, должно быть уникальным в пределах этого пространства.
Однако в разных пользовательских пространствах могут встречаться члены с одинаковыми именами.
Имя члена пространства имен автоматически дополняется, или квалифицируется, именем этого пространства. Например, имя класса matrix, объявленное в пространстве cplusplus_primer, становится cplusplus_primer::matrix, а имя функции inverse() превращается в

cplusplus_primer::inverse().

Члены cplusplus_primer могут использоваться в программе с помощью спецификации имени:

void func( cplusplus_primer::matrix &m )
{
   // ...
   cplusplus_primer::inverse(m);
   return m;
}

Если в другом пользовательском пространстве имен (скажем, DisneyFeatureAnimation) также существует класс matrix и функция inverse() и мы хотим использовать этот класс вместо объявленного в пространстве cplusplus_primer, то функцию func() нужно модифицировать следующим образом:

void func( DisneyFeatureAnimation::matrix &m )
{
   // ...
   DisneyFeatureAnimation::inverse(m);
   return m;
}

Конечно, каждый раз указывать специфицированные имена типа

namespace_name::member_name

неудобно. Поэтому существуют механизмы, позволяющие облегчить использование пространств имен в программах. Это псевдонимы пространств имен, using-объявления и using-директивы. (Мы рассмотрим их в разделе 8.6.)
8.5.1. Определения пространства имен

Определение пользовательского пространства имен начинается с ключевого слова namespace, за которым следует идентификатор. Он должен быть уникальным в той области видимости, в которой определяется данное пространство; наличие другой сущности с тем же именем является ошибкой. Конечно, это не означает, что проблема засорения глобального пространства решена полностью, но существенно помогает в ее решении.
За идентификатором пространства имен следует блок в фигурных скобках, содержащий различные объявления. Любое объявление, допустимое в области видимости глобального пространства, может встречаться и в пользовательском: классы, переменные (вместе с инициализацией), функции (вместе со своими определениями), шаблоны.
Помещая объявление в пользовательское пространство, мы не меняем его семантики. Единственное отличие состоит в том, что имена, вводимые такими объявлениями, включают в себя имя пространства, внутри которого они объявлены. Например:

namespace cplusplus_primer {
   class matrix { /* ... */ };
   void inverse ( matrix & );
   matrix operator+ ( const matrix &ml, const matrix &m2 )
   {/* ... */ }
   const double pi = 3.1416;
}

Именем класса, объявленного в пространстве cplusplus_primer, будет

cplusplus_primer::matrix

Именем функции

cplusplus_primer::inverse()

Именем константы

cplusplus_primer::pi

Имя класса, функции или константы расширяется именем пространства, в котором они объявлены. Такие имена называют квалифицированными.
Определение пространства имен не обязательно должно быть непрерывным. Например, предыдущее пространство могло быть определено таким образом:

namespace cplusplus_primer {
   class matrix { /* ... */ };
   const double pi = 3.1416;
}

namespace cplusplus_primer {
  void inverse ( matrix & );
  matrix operator+ ( const matrix &ml, const matrix &m2 )
  {/* ... */ }
}

Два приведенных примера эквивалентны: оба задают пространство имен cplusplus_primer, содержащее класс matrix, функцию inverse(), константу pi и operator+(). Определение пространства имен может состоять из нескольких соединенных частей.
Последовательность

namespace namespace_name {

задает новое пространство, если имя namespace_name не совпадает с одним из ранее объявленных. В противном случае новые объявления добавляются в старое пространство.
Возможность разбить пространство имен на несколько частей помогает при организации библиотеки. Ее исходный код легко разделить на интерфейсную часть и реализацию. Например:

// Эта часть пространства имен
// определяет интерфейс библиотеки

namespace cplusplus_primer {
  class matrix { /* ... */ };
  const double pi = 3.1416;
  matrix operator+ ( const matrix &ml, const matrix &m2 );
  void inverse ( matrix & );
}

// Эта часть пространства имен
// определяет реализацию библиотеки

namespace cplusplus_primer {
  void inverse ( matrix &m )
  { /* ... */ }
  matrix operator+ ( const matrix &ml, const matrix &m2 )
  { /* ... */ }
}

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

// ---- primer.h ----
namespace cplusplus_primer {
   class matrix { /*... */ };
   const double pi = 3.1416;
   matrix operator+ ( const matrix &m1, const matrix &m2 );
   void inverse( matrix & );
}

// ---- primer.C ----
#include "primer.h"
namespace cplusplus_primer {
  void inverse( matrix &m )
  { /* ... */ }
  matrix operator+ ( const matrix &m1, const matrix &m2 )
  { /* ... */ }
}

Программа, использующая эту библиотеку, выглядит так:

// ---- user.C ----
// определение интерфейса библиотеки
#include "primer.h"
void func( cplusplus_primer::matrix &m )
{
  //...
  cplusplus_primer: :inverse( m );
  return m;
}

Подобная организация программы обеспечивает модульность библиотеки, необходимую для сокрытия реализации от пользователей, в то же время позволяя без ошибок скомпилировать и связать файлы primer.C и user.C в одну программу.
8.5.2. Оператор разрешения области видимости

Имя члена пользовательского пространства дополняется поставленным спереди именем этого пространства и оператором разрешения области видимости (::). Использование неквалифицированного члена, например matrix, является ошибкой. Компилятор не знает, к какому объявлению относится это имя:

// определение интерфейса библиотеки
#include "primer.h"

// ошибка: нет объявления для matrix
void func( matrix &m );

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

// определение интерфейса библиотеки
#include "primer.h"

class matrix { /* пользовательское определение */ };
// правильно: глобальный тип matrix найден
void func( matrix &m );

то определение класса matrix компилятор находит в глобальной области видимости и программа компилируется без ошибок. Поскольку объявление matrix как члена пространства имен cplusplus_primer скрыто в этом пространстве, оно не конфликтует с классом, объявленным в глобальной области видимости.
Именно поэтому мы говорим, что пространства имен решают проблему засорения глобального пространства: имена их членов невидимы, если имя пространства не указано явно, с помощью оператора разрешения области видимости. Существуют и другие механизмы, позволяющие сделать объявление члена пространства имен видимым вне его. Это using-объявления и using-директивы. Мы рассмотрим их в следующем разделе.
Отметим, что оператор области видимости может быть использован и для того, чтобы сослаться на элемент глобального пространства имен. Поскольку это пространство не имеет имени, запись

::member_name

относится к его элементу. Такой способ полезен для указания членов глобального пространства, если их имена оказываются скрыты именами, объявленными во вложенных локальных областях видимости.
Следующий пример демонстрирует использование оператора области видимости для обращения к скрытому члену глобального пространства имен. Функция вычисляет последовательность чисел Фибоначчи. В программе два определения переменной max. Глобальная переменная указывает максимальное значение элемента последовательности, при превышении которого вычисление прекращается, а локальная – желаемую длину последовательности при данном вызове функции. (Напоминаем, что параметры функции относятся к ее локальной области видимости.) Внутри функции должны быть доступны обе переменных. Однако неквалифицированное имя max ссылается на локальное объявление этой переменной. Чтобы получить глобальную переменную, нужно использовать оператор разрешения области видимости ::max. Вот текст программы:

#include <iostream>
const int max = 65000;
const int lineLength = 12;

void fibonacci( int max )
{
   if ( max < 2 ) return;
    cout << "0 1 ";
   int v1 = 0, v2 = 1, cur;
   for ( int ix = 3; ix <= max; ++ix ) {
      cur = v1 + v2;
      if ( cur > ::max ) break;
      cout << cur << " ";
      vl = v2;
      v2 = cur;
      if (ix % "lineLength == 0) cout << end"!;
   }
}

Так выглядит функция main(), вызывающая fibonacci():

#include <iostream>
void fibonacci( int );
int main() {
   cout << "Числа Фибоначчи: 16\n";
   fibonacci( 16 );
   return 0;
}

Результат работы программы:

Числа Фибоначчи: 16
0 1 1 2 3 5 8 13 21 34 55 89
144 233 377 610

8.5.3. Вложенные пространства имен

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

// ---- primer.h ----
namespace cplusplus_primer {
    // первое вложенное пространство имен:
    // матричная часть библиотеки
    namespace MatrixLib {
        class matrix { /* ... */ };
        const double pi = 3.1416;
        matrix operators+ ( const matrix &ml, const matrix &m2 );
        void inverse( matrix & );
        // ...
    }
    // второе вложенное пространство имен:
    // зоологическая часть библиотеки
    namespace AnimalLib {
        class ZooAnimal { /* ... */ };
        class Bear : public ZooAnimal { /* ... */ };
        class Raccoon : public Bear { /* ... */ };
        // ...
    }
}

Пространство имен cplusplus_primer содержит два вложенных: MatrixLib и AnimalLib.
cplusplus_primer предотвращает конфликт между именами из нашей библиотеки и именами из глобального пространства вызывающей программы. Вложенность позволяет делить библиотеку на части, в которых сгруппированы связанные друг с другом объявления и определения. MatrixLib содержит сущности, имеющие отношение к классу matrix, а AnimalLib – к классу ZooAnimal.
Объявление члена вложенного пространства скрыто в этом пространстве. Имя такого члена автоматически дополняется поставленными спереди именами самого внешнего и вложенного пространств.
Например, класс, объявленный во вложенном пространстве MatrixLib, имеет имя
cplusplus_primer::MatrixLib::matrix
а функция
cplusplus_primer::MatrixLib::inverse
Программа, использующая члены вложенного пространства cplusplus_primer::MatrixLib, выглядит так:

#include "primer.h"
// да, это ужасно...
// скоро мы рассмотрим механизмы, облегчающие
// использование членов пространств имен!
void func( cplusplus_primer::MatrixLib::matrix &m )
{
  // ...
  cplusplus_primer::MatrixLib::inverse( m );
  return m;
}

Вложенное пространство имен является вложенной областью видимости внутри пространства, содержащего его. В процессе разрешения имен вложенные пространства ведут себя так же, как вложенные блоки. Когда некоторое имя употребляется в пространстве имен, поиск его объявление проводится во всех объемлющих пространствах. В следующем примере разрешение имени Type происходит в таком порядке: сначала ищем его в пространстве имен MatrixLib, затем в cplusplus_primer и наконец в глобальной области видимости:

typedef double Type;
namespace cplusplus_primer {
    typedef int Type; // скрывает ::Type
    namespace MatrixLib {
        int val;
        // Type: объявление найдено в cplusplus_primer
        int func(Type t) {
            double val; // скрывает MatrixLib::val
            val = ...;
        }
        // ...
    }
}

Если некоторая сущность объявляется во вложенном пространстве имен, она скрывает объявление одноименной сущности из объемлющего пространства.
В предыдущем примере имя Type из глобальной области видимости скрыто объявлением Type в пространстве cplusplus_primer. При разрешении имени Type, упоминаемого в MatrixLib, оно будет найдено в cplusplus_primer, поэтому у функции func() параметр имеет тип int.
Аналогично сущность, объявленная в пространстве имен, скрывается одноименной сущностью из вложенной локальной области видимости. В предыдущем примере имя val из MatrixLib скрыто новым объявлением val. При разрешении имени val внутри func() будет найдено его объявление в локальной области видимости, и потому присваивание в func() относится именно к локальной переменной.
8.5.4. Определение члена пространства имен

Мы видели, что определение члена пространства имен может появиться внутри определения самого пространства. Например, класс matrix и константа pi появляются внутри вложенного пространства имен MatrixLib, а определения функций operator+() и inverse() приводятся где-то в другом месте текста программы:

// ---- primer.h ----
namespace cplusplus_primer {
    // первое вложенное пространство имен:
    // матричная часть библиотеки
    namespace MatrixLib {
        class matrix { /* ... */ };
        const double pi = 3.1416;
        matrix operators+ ( const matrix &ml, const matrix &m2 );
        void inverse( matrix & );
        // ...
    }
}

Член пространства имен можно определить и вне соответствующего пространства. В таком случае имя члена должно быть квалифицировано именами пространств, к которым он принадлежит. Например, если определение функции operator+() помещено в глобальную область видимости, то оно должно выглядеть следующим образом:

 // ---- primer.C ----
#include "primer.h"

// определение в глобальной области видимости
cplusplus_primer::MatrixLib::matrix
cplusplus_primer::MatrixLib::operator+
( const matrix& ml, const matrix &m2 )
{ /* ... */ }

Имя operator+() квалифицировано в данном случае именами пространств cplusplus_primer и MatrixLib. Однако обратите внимание на тип matrix в списке параметров operator+(): употреблено неквалифицированное имя. Как такое может быть?
В определении функции operator+() можно использовать неквалифицированные имена для членов своего пространства, поскольку определение принадлежит к его области видимости. При разрешении имен внутри функции operator+() используется MatrixLib. Заметим, однако, что в типе возвращаемого значения все же нужно указывать квалифицированное имя, поскольку он расположен вне области видимости, заданной определением функции:

cplusplus_primer::MatrixLib::operator+

В определении operator+() неквалифицированные имена могут встречаться в любом объявлении или выражении внутри списка параметров или тела функции. Например, локальное объявление внутри operator+() способно создать объект класса matrix:

// ---- primer.C ----
#include "primer.h"

cplusplus_primer::MatrixLib::matrix
cplusplus_primer::MatrixLib::operator+
( const matrix &ml, const matrix &m2 )
{
    // объявление локальной переменной типа
    // cplusplus_primer::MatrixLib::matrix
    matrix res;
    // вычислим сумму двух объектов matrix
    return res;
}

Хотя члены могут быть определены вне своего пространства имен, такие определения допустимы не в любом месте. Их разрешается помещать только в пространства, объемлющие данное. Например, определение operator+() может появиться в глобальной области видимости, в пространстве имен cplusplus_primer и в пространстве MatrixLib. В последнем случае это выглядит так:

// ---- primer.C --
#include "primer.h"

namespace cplusplus_primer {
  MatrixLib::matrix MatrixLib::operator+
  ( const matrix &ml, const matrix &m2 ) { /* ... */ }
}

Член может определяться вне своего пространства только при условии, что ранее он был объявлен внутри. Последнее приведенное определение operator+() было бы ошибочным, если бы ему не предшествовало объявление в файле primer.h:

namespace cplusplus_primer {
   namespace MatrixLib {
      class matrix { /*...*/ };
      // следующее объявление не может быть пропущено
      matrix operator+ ( const matrix &ml, const matrix &m2 );
      // ...
   }
}
Категория: С++ | Добавил: r2d2 (29.09.2011)
Просмотров: 249 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Born in Ussr
Залогиниться
Турниры

/j clan ussr /j clan cccp