2011-12-26 2 views
3

Я знакомлюсь с C++, и у меня возникла проблема с управлением памятью. В C, когда бы я не захотел зарезервировать память для любого количества элементов, независимо от типа, я бы просто вызвал malloc(), а затем инициализировал вручную (через цикл) в зависимости от того, какое значение я хотел. С C++ new все автоматически инициализируется.Выделение памяти без инициализации в C++

Проблема в том, у меня есть класс BattlePoint, который идет немного что-то вроде этого:

class BattlePoint { 
public: 
    BattlePoint(int x, int y) : x(x), y(y) { }; 
    bool operator==(const BattlePoint &right); 
    virtual ~BattlePoint(); 

private: 
    int x, y; 
}; 

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

BattleShip::BattleShip(BattlePoint start, enum shipTypeSize size, enum shipOrientation orientation) : size(size), orientation(orientation) { 
    points = new BattlePoint[size]; // Here be doubts. 
} 

Итак, мне нужна точка моего линкора провести массив BattlePoints, каждый из которых с различными значениями инициализаций (такие как 0,1, 0,2, 0,3 и т. Д.).

Вопрос: как я мог выделить свою память неинициализированной?

Джулиан,

PS: Я не сделал каких-либо испытаний в отношении пути new работы, я просто читал Wikipedia's article on it, который говорит:

В языке программирования C++, а также во многих C++ основанный на языках, новый - это языковая конструкция, которая динамически выделяет память на куче и инициализирует память с помощью конструктора . За исключением формы, называемой «размещение нового», новый пытается выделить достаточное количество памяти в куче для новых данных. Если успешно, он инициализирует память и возвращает адрес в новую выделенную и инициализированную память . Однако, если новый не может выделить памяти в куче, он выдает исключение типа std :: bad_alloc. Это устраняет необходимость явно проверить результат распределения. Вызов для удаления, который вызывает деструктор и возвращает память , выделенную новой копией в кучу, должен быть сделан для каждого вызова нового во избежание утечки памяти.

Размещение нового должно быть решением, но в нем не упоминается, как это сделать.

P.S. 2: Я знаю, что это можно сделать с помощью векторного класса stdlib, но я избегаю этого специально.

+0

Что не позволяет вам использовать malloc? –

+0

Тот факт, что я использую C++, что заставляет меня хотеть использовать языковое решение. 'malloc' - это C. –

+0

Это нормально, если вы хотите использовать строго языковые функции. Но malloc также является частью C++, как новый оператор. Как вы указали в своих комментариях, вы хотите избежать как векторных конструкторов stdlib, так и конструкторов по умолчанию. Итак, почему бы вам не использовать простой бросок и malloc? –

ответ

7

Вам необходимо использовать std::vector. В этом случае вы можете push_back, что бы вы ни хотели, например.

std::vector<BattlePoint> x; 
x.push_back(BattlePoint(1, 2)); 

Если вы когда-нибудь найти себя с помощью new[], delete или delete[], реорганизовать программу немедленно удалить такие. Они ужасно опасны практически всеми возможными способами. Вместо этого используйте классы управления ресурсами, такие как std::unique_ptr, std::vector и std::shared_ptr.

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

Редактировать: Моя ошибка, я не видел самую последнюю строку вашего вопроса. Адресация:

P.S. 2: Я знаю, что это можно сделать с помощью векторного класса stdlib, но я не знаю, как это сделать, но я .

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

+0

почему они так небезопасны?, Эта же модель управления памятью была использована программистами C уже давно, и хотя она приносит проблемы, кажется, что она работает ... –

+2

@Julian: C не имеет исключения. И даже тогда было трудным хлопотом управлять памятью в C. – Xeo

+3

@Julian: Проблема не только в исключениях, но и в C++ имеет гораздо более высокие стандарты безопасности. Программы на C++, в общем, должны быть гарантированы, чтобы никогда не терять и не удалять двойное удаление во время компиляции, что более чем возможно с использованием классов управления памятью. Это намного более высокий стандарт, чем «Ну, я думаю, это сработало на этот раз ...» C. 'vector' не только позволит вам построить все, что вы хотите, и защитить вас от исключений, но и защитит вас от утечек и двойных удалений. – Puppy

4

баллов = новый BattlePoint [размер]; // Здесь есть сомнения.
P.S. 2: Я знаю, что это можно сделать с помощью векторного класса stdlib, но я избегаю этого специально.

Скорее всего, будут сомнения! Используйте std::vector. Почему бы и нет? нет причина не использовать std::vector, в частности если это решает вашу проблему.

std::vector<BattlePoint> bpoints; 
bpoints.reserve(size); // there, only alloc'd memory, not initialized it. 
bpoints.push_back(some_point); // still need to use push_back to initialize it 

Я уверен, что вопрос придет - как же std::vector только Alloc память ?! operator new - ответ. Это оператор, который вызывается для выделения памяти, когда вы используете new. new предназначен для построения и инициализации, а operator new предназначен для размещения (поэтому вы можете перегрузить его).

BattlePoint* bpoints = ::operator new(size); // happens in reserve 
new (bpoints[index]) BattlePoint(some_x, some_y); // happens in push_back 
3

Чтобы повторить приведенные выше ответы, я бы, безусловно, указал вам на std::vector, поскольку это наилучшее решение. Управление вашими собственными динамическими массивами в C++ - почти - это хорошая идея, и это почти не нужно.

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

class BattlePoint { 
public: 
    // default constructor, default initialize to 0,0 
    BattlePoint() x(0), y(0) {}; 

    BattlePoint(int x, int y) : x(x), y(y) { }; 
    bool operator==(const BattlePoint &right); 
    virtual ~BattlePoint(); 

    // mutator functions allow you to modify the classes member values 
    void set_x(int x_) {x = x_;} 
    void set_y(int y_) {y = y_;} 

private: 
    int x, y; 
}; 

Затем вы можете инициализировать это, как вы привыкли в C :

BattlePoint* points = new BattlePoint[100]; 

for(int x = 0; x < 100; ++x) 
{ 
    points->set_x(x); 
    points->set_y(x * 2); 
} 

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

Так как вы просили :)

BattlePoint Создать свой класс снова с конструктором и мутаторами по умолчанию, однако на этот раз оставить мутаторы частного, и объявить функцию друга, чтобы использовать их:

class BattlePoint { 
public: 
    // default constructor, default initialize to 0,0 
    BattlePoint() x(0), y(0) {}; 

    BattlePoint(int x, int y) : x(x), y(y) { }; 
    bool operator==(const BattlePoint &right); 
    virtual ~BattlePoint(); 

private: 
    // mutator functions allow you to modify the classes member values 
    void set_x(int x_) {x = x_;} 
    void set_y(int y_) {y = y_;} 

    int x, y; 

    friend void do_initialize_x_y(BattlePoint*, int, int); 
}; 

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

// BattlePoint_Initialize.h 
BattlePoint* create_battle_point_array(size_t count, int* x, int* y); 

Эта функция получает определенные в файле реализации, вместе с нашим другом функции, что мы будем «скрыть» от внешнего мира:

// BattlePoint_Initialize.cpp 
#include <BattlePoint_Initialize.h> 

namespace 
{ 
    // by putting this function in an anonymous namespace it is only available 
    // to this compilation unit. This function can only be called from within 
    // this particular file. 
    // 
    // technically, the symbols are still exported, but they are mangled badly 
    // so someone could call this, but they would have to really try to do it 
    // not something that could be done "by accident" 
    void do_initialize_x_y(BattlePoint* bp, int x, int y) 
    { 
     bp->set_x(x); 
     bp->set_y(y); 
    } 
} 

// caution, relies on the assumption that count indicates the number of 
// BattlePoint objects to be created, as well as the number of valid entries 
// in the x and y arrays 
BattlePoint* create_battle_point_array(size_t count, int* x, int* y) 
{ 
    BattlePoint* bp_array = new BattlePoint[count]; 

    for(size_t curr = 0; curr < count; ++curr) 
    { 
     do_initialize_x_y(bp_array[curr], x[curr], y[curr]); 
    } 

    return bp_array; 
} 

Так что у вас есть. Очень запутанный способ удовлетворить ваши основные требования.

В то время как create_battlepoint_array() можно было бы назвать в любом случае, он на самом деле не способен модифицировать уже созданный объект BattlePoint. Функция do_initialize_x_y() по характеру скрытия в анонимном namespace, скрытом за кодом инициализации, не может быть вызвана нигде в вашей программе. Фактически, как только объект BattlePoint был создан (и инициализирован в два этапа), его нельзя изменить дальше.

+0

Да, но это означало бы наличие дополнительных функций И ​​позволяя любому коду изменять мои xs и ys после инициализации, чего я пытаюсь избежать. –

+0

Дополнительные функции, да, позволяя любому коду изменять значения X, Y, нет. Новые функции могут быть частными для блока компиляции, инициализирующего объекты BattlePoint. Любопытно, почему вы хотите избежать 'std :: vector <>' хотя? – Chad

+0

Я хочу, чтобы код был самым сложным способом, каким я могу, чтобы узнать, как это может быть сложнее, и узнать, как язык работает в этом процессе. Сделать вещи легче оттуда не так сложно. Кстати, как я могу сделать их приватными только тем, кто их инициализирует? –

2

comp.lang.c++ FAQ имеет смысл сказать по этому вопросу, в том числе пытаться отговорить вас от использования нового места размещения - но если вы действительно настаиваете, у него есть полезный раздел на placement new и все его подводные камни.

+0

Спасибо, прочитаю. –

Смежные вопросы