LINUX.ORG.RU

Выделение массива динамического размера в классе C++

 


0

2

Можно как-то так сделать:

class E {
  int i[h];
 public:
  Map(int h) {}
};

? Думаю вы поняли о чём речь. Хочу выделять массив динамического размера не используя динамическую память.



Последнее исправление: yufhgigibi (всего исправлений: 4)
Ответ на: комментарий от yufhgigibi

Размер структуры должен быть известен в момент компиляции. Как вариант, можно сделать по аналогии с SSO — хранить в структуре статичный буфер, а если нужно больше, то уже динамически выделять.

thunar ★★★★★
()

не используя динамическую память.

Что тебя смущает?

Используй vector и не парься.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)

Используйте шаблон или возьмите готовый std::array<int n>. Компилятор должен знать размер структуры, иначе никак.

anonymous
()

в c++17 появился memory_resource, std::pmr::string std::pmr::vector ... как раз для этого. там даже ресурс есть, который выделяет блоками.

выделения куска на стеке ничего не дает. это важно когда нужно создавать и уничтожать много маленьких. с таким же успехом можно выделить и в куче. на стеке нужно только если для чего-то конкретного выделяешь, например pimpl хочешь создать внутри класса.

zerhud
()

Можно использовать то, что называется Операция new с размещением:

#include <new>

class A {
public:
  A() {
    pointer_ = new (buffer_) double[10];
    pointer_[0] = 0.0;
    pointer_[1] = 0.1;
    pointer_[2] = 0.2;
  }
  ~A() {
    // Не делать !!!
    // delete[] pointer_;
  }

private:
  double buffer_[512];
  double *pointer_{};
};

int main() { A a; }

Но это так, для теории. На практике у меня не возникало случаев, когда это требуется. Кстати, может кто-нибудь применял, расскажите пожалуйста в каких случаях?

rumgot ★★★★★
()

Выдели 1 раз большой кусок и напиши выделение из него уже по кусочкам нужным тебе (правда придётся написать своё управление памятью). Если тебе нужно выделять только увеличивая то гугли линейный аллокатор, пишется быстро, работает быстро 50 строчек кода на Си и готово.

anonymous
()
Ответ на: комментарий от rumgot

ЕМНИП, placement new - это единственный правильный способ инициализации нетривиальных объектов в объединениях (union):

#include <string>
#include <iostream>

int main() {
	union MyUnion {
		MyUnion() {}
		~MyUnion() {}
		std::string a;
		std::string b;
	} u;
	
// 	segmentation fault
//	u.a = std::string {"Hello, World!"};
	new (&u.a) std::string {"Hello, World!\n"};
	std::cout << u.a;
}

moonmadness
()

Используй std::vector, это же C++. Вот когда возможностей/скорости вектора перестанет хватать, тогда и ищи другое решение.

dvetutnev
()
Ответ на: комментарий от dvetutnev

Ему наверняка очередной Столяров в мозг насрал, что крестовые контейнеры это якобы зло.

anonymous
()
Ответ на: комментарий от moonmadness

Хм. На Microsoft Visual C++ Compiler 16.8.30711.63 (amd64) не падает.

#include <iostream>
#include <string>

int main() {
  union MyUnion {
    MyUnion() {}
    ~MyUnion() {}
    std::string a;
    std::string b;
  } u;

  // segmentation fault не наблюдаю
  u.a = std::string{"Hello, World!"};
  // new (&u.a) std::string{"Hello, World!\n"};
  std::cout << u.a;
}
rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 3)
Ответ на: комментарий от rumgot

У тебя UB. Правда, не очень явно описанное в стандарте. Если бы ты делал new (buffer_) double[10]; вне конструктора (и деструктора), то пришиб бы и a.buffer_, и весь a.

Но т.к. ты делаешь это в конструкторе, когда lifetime у объекта a ещё не начался, то ХЗ что значит такое создание объекта типа double[10] поверх объекта a. Неявное UB потому что в [class.cdtor] не описано что в этом случае будет.

anonymous
()
Ответ на: комментарий от moonmadness

И какой объект, u.a или u.b, ты «инициализируешь» своим placement new? Ответ «u.a, потому что в new-placement указан &u.a» неправильный. (Вообще placement new не инициализирует существующий объект, а создаёт новый, но хер с терминологией).

(Кстати. Даже если бы в union был только один NSDM типа std::string, скажем, MyUnion::a, то u.a, если верить стандарту, не стал бы обозначать новый объект созданный placement new.)

anonymous
()
Ответ на: комментарий от anonymous

У тебя UB. Правда, не очень явно описанное в стандарте. Если бы ты делал new (buffer_) double[10]; вне конструктора (и деструктора), то пришиб бы и a.buffer_, и весь a.

Но т.к. ты делаешь это в конструкторе, когда lifetime у объекта a ещё не начался, то ХЗ что значит такое создание объекта типа double[10] поверх объекта a. Неявное UB потому что в [class.cdtor] не описано что в этом случае будет.

Вообще я этими конструкциями не пользовался никогда. Поэтому с абсолютной уверенностью утверждать не стану. Но. Члены объекта создаются ДО вызова конструктора. В чем проблема? А операция new с размещением (по умолчанию) - так вообще ничего не выделяет фактически, а просто использует переданный ей адрес и преобразует его в указатель требуемого типа.

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от anonymous

Я там вообще то написал про отсутствие segfault, а не про UB.

rumgot ★★★★★
()
Ответ на: комментарий от AlexVR

Конструктор не осилил написать в одну строку и решил строить из себя петуха?

anonymous
()
Ответ на: комментарий от rumgot

Вообще я этими конструкциями не пользовался никогда. Поэтому с абсолютной уверенностью утверждать не стану. Но. Члены объекта создаются ДО вызова конструктора. В чем проблема?

Создание нового объекта поверх другого (других) прибивает (заканчивает lifetime) старый(-ые) объект(ы), только если новый объект не nested within старого (см. тута.

При этом ХЗ что будет если lifetime старого ещё не начался. [basic.life]/4 говорит «The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime» так что, видимо, пункт про реюзинг стораджа не применим к объекту который ещё не within lifetime. Также, [basic.life] в нескольких местах намекает что для описания поведения во время конструирования объекта надо смотреть в [class.cdtor]. [class.cdtor] не описывает что будет если реюзнуть сторадж объекта under construction/destruction, что, думаю, надо трактовать как неявное UB.

anonymous
()
Ответ на: комментарий от CatsCantFly

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

anonymous
()
Ответ на: комментарий от rumgot

Надо читать, что в стандарте пишут, я сам в плюсах не силен, хотя вот тут пишут:

If members of a union are classes with user-defined constructors and destructors, to switch the active member, explicit destructor and placement new are generally needed: 
moonmadness
()
Ответ на: комментарий от anonymous

Ну хз конечно. Но ведь new с размещением ничего толком не делает. Просто приводит одно к другому. Может это не попадает под строку:

the storage which the object occupies is released, or is reused by an object that is not nested

?

rumgot ★★★★★
()
Ответ на: комментарий от rumgot

Ну хз конечно. Но ведь new с размещением ничего толком не делает. Просто приводит одно к другому.

new-expression, в т.ч. вызывающее non-allocating формы оператора new, создаёт новый объект. А не «приводит одно к другому»

Может это не попадает под строку:

Да вроде другого reuse кроме создания объекта в этом storage не существует. В [basic.life] есть пример с такой строкой

new (this) D2;    // reuses storage --- ends the lifetime of *this
anonymous
()
Ответ на: комментарий от anonymous

Да вроде другого reuse кроме создания объекта в этом storage не существует. В [basic.life] есть пример с такой строкой

new (this) D2; // reuses storage — ends the lifetime of *this

Вот полный текст оттуда:

#include <cstdlib>

struct B {
  virtual void f();
  void mutate();
  virtual ~B();
};

struct D1 : B { void f(); };
struct D2 : B { void f(); };

void B::mutate() {
  new (this) D2; // reuses storage — ends the lifetime of *this
  f(); // undefined behavior
  ... = this; // OK, this points to valid memory
}

void g() {
  void* p = std::malloc(sizeof(D1) + sizeof(D2));
  B* pb = new (p) D1;
  pb->mutate();
  *pb; // OK: pb points to valid memory
  void* q = pb; // OK: pb points to valid memory
  pb->f(); // undefined behavior, lifetime of *pb has ended
}

тут не эквивалентный пример, тут на месте всего объекта создается новый. А в моем случае я запрашиваю double[10] на месте члена.

new-expression, в т.ч. вызывающее non-allocating формы оператора new, создаёт новый объект. А не «приводит одно к другому»

pointer_ = new (buffer_) double[10];

Тут ведь толком ничего не создается. Фактически тут buffer_ просто приводится к double* (возможно там еще что-то выполняется, не скажу).

rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 2)
Ответ на: комментарий от rumgot

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

Какая разница, на месте всего или не всего? И с чего ты вообще решил, что на месте всего? Может, sizeof(D1) > sizeof(D2). Не просто так malloc-ом выделяют sizeof(D1) + sizeof(D2) (хотя вообще достаточно max(sizeof(D1), sizeof(D2)).

Тут ведь толком ничего не создается. Фактически тут buffer_ просто приводится к double* (не уверен, что-там еще выполняется кроме этого).

Ты бы ещё ассемблерными листингами кидаться начал. «Толком», «фактически» и т.п. это не те слова которыми говорят про поведение с т.з. стандарта.

anonymous
()
Ответ на: комментарий от yufhgigibi

используй шаблон. когда ты создаёшь массив переменной длины в классе, то изменяется размер объекта. class myclass { int x[2]; } и class myclass { int x[3]; } - это два разных типа разного размера, поэтому шаблон: template <int n> class myclass { int x[n]; }.

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

anonymous
()
Ответ на: комментарий от anonymous

Проблема только одна – при использовании такого типа в программе нужно писать не MyClass *m, а MyClass<42> *m. Соответственно в параметрах функций это не выглядит приятно. Впрочем, у меня таких функций и нет.

Да, это именно то решение которое я хотел, забыл про шаблоны. Спасибо.

yufhgigibi
() автор топика
Ответ на: комментарий от yufhgigibi

это число во время выполнения появляется? возьми обычный vector тогда и мувни его в свой класс, а конструктор напиши так: myclass(std::vector && v).

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.