Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Оператор размещения 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" );

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

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

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

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

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.)

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

Определение пользовательского пространства имен начинается с ключевого слова 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 в одну программу.


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 301; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.061 с.)
Главная | Случайная страница | Обратная связь