Создайте массив объектов класса

Рассмотрим следующий класс

class test
{
public:
test(int x){ cout<< "test \n"; }

};

Теперь я хочу создать массив из 50 объектов класса test. Я не могу изменить тест класса.

Объекты можно создавать в куче или стеке.

В этом случае создание объектов в стеке невозможно, поскольку в классе нет конструктора по умолчанию.

test objs(1)[50]; /// Error...

Теперь мы можем подумать о создании объектов в куче вот так ...

test ** objs = NULL;
objs = (test **) malloc( 50 * sizeof (test *));
for (int i =0; i<50 ; ++ i)
{
   objs[i] = new test(1);
}

Я не хочу использовать malloc. Есть ли другой способ?

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
592
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вам не нужен malloc (). Вы также можете использовать new для массива указателей:

    test **objs = new test* [50];

Просто чтобы уточнить: new выполняет динамическое выделение памяти в куче так же, как malloc (). Разница в том, что он встроен в язык C++, поэтому также может вызывать конструкторы.

j_random_hacker 17.01.2009 11:18

Зачем нужен массив?

std::vector<test*> v(50);

Или как @j_random_hacker предложил в комментариях:

std::vector<test> v(50, test(1));

Пример:

/** g++ -Wall -o vector_test *.cpp && vector_test */
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

struct Test {
  int value;
  Test(int x) : value(x) 
  {
    std::cout << "Test(" << value << ")" << " ";
  }
  operator int() const
  {
    std::cout << "int(" << value << ")" << " ";
    return value;
  }
};

int main()
{
  using namespace std;

  vector<Test> v(5, Test(1));

  cout << endl;
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
  cout << endl;

  v[1] = 2;
  v[2].value = 3;

  cout << endl;
  copy(v.begin(), v.end(), ostream_iterator<int>(cout, " "));
  cout << endl;

  return 0;
}

Выход:

Test(1) 
int(1) 1 int(1) 1 int(1) 1 int(1) 1 int(1) 1 
Test(2) 
int(1) 1 int(2) 2 int(3) 3 int(1) 1 int(1) 1 

Разве вы не имеете в виду "std :: vector <test> v (50, test (1))"?

j_random_hacker 17.01.2009 11:22

@j_random_hacker: test* используется для включения экземпляров подклассов test в вектор (привычка ООП).

jfs 17.01.2009 14:42

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

j_random_hacker 17.01.2009 16:46
Ответ принят как подходящий

Вы не можете создать массив объектов, как в Foo foo  [N], без конструктора по умолчанию. Это часть языковой спецификации.

Либо делать:

test * objs [50];
for() objs[i] = new test(1).

Вам не нужен malloc (). Вы можете просто объявить массив указателей.

c++decl> explain int * objs [50]
declare objs as array 50 of pointer to int

Но вы, вероятно, должны иметь какое-то автоматическое уничтожение типа RAII.


ИЛИ ЖЕ подкласс контрольная работа публично:

class TempTest : public test
{
public:
  TempTest() : test(1) {}
  TempTest(int x) : test(x) {}
  TempTest(const     test & theTest ) : test(theTest) {}
  TempTest(const TempTest & theTest ) : test(theTest) {}
  test & operator=( const     test & theTest ) { return test::operator=(theTest); }
  test & operator=( const TempTest & theTest ) { return test::operator=(theTest); }
  virtual ~TempTest() {}
};

а потом:

TempTest  array[50];

Вы можете рассматривать каждый объект TempTest как объект контрольная работа.

Примечание. Operator = () и конструктор копирования не наследуются, поэтому при необходимости укажите заново.

Хороший пост в целом, но просто для пояснения: вы говорите: «Вам не нужен malloc ()», но вам нужен «новый», верно? (Если вы не являетесь подклассом.) Все, что я имел в виду, это то, что, вероятно, когда OP сказал «Я не хочу использовать malloc ()», он действительно имел в виду «Я не хочу использовать динамическое распределение памяти».

j_random_hacker 17.01.2009 11:17

OP запрограммировал массив указателей. Я кладу этот массив в стек. Элементы тоже могут быть в стеке, но это требует реализации отвратительный. test * objs [50]; тест obj0 (0), obj1 (1), ..., objN (N); objs [0] = & obj0; objs [1] = & obj1; ...; objs [N] = & objN;

Mr.Ree 18.01.2009 04:05

Приложение: Напоминание: если у вас есть массив указателей, вы можете получить доступ через * (objs [i]) или использовать objs [i] -> method ().

Mr.Ree 18.01.2009 04:06

Будьте осторожны с тем, как используется «массив». Передача массива в функцию, ожидающую параметра «test * p», будет компилироваться, но если sizeof (TempTest)! = Sizeof (test) (из-за добавления vtable в TempTest), то «++ p» не будет указывать на следующий элемент. Код должен утверждать, что sizeof (test) == sizeof (TempTest).

Richard Corden 21.01.2009 12:46

Я думаю, что другие респонденты слишком буквально трактуют этот вопрос.

Если все, что вам действительно нужно, это создать «группу» из 50 объектов, которые можно рассматривать как массив, то, безусловно, самый простой и наиболее удобный способ выполнить то, что вы пытаетесь сделать, это:

std::vector<test> objs(50, test(1));

Это объявляет vector из 50 объектов, каждый из которых является копией test(1). vector - это, по сути, растущий массив C++; хотя вам может не понадобиться возможность роста, тот факт, что он может быть вызван с помощью конструктора с 2 аргументами, который копирует каждый элемент, здесь полезен.

Вы можете использовать это более или менее точно так же, как массив - например, 5-й элемент - objs[4]. Производительность тоже такая же - стандарт C++ гарантирует, что внутри элементы хранятся в непрерывном массиве.

Здесь на помощь может прийти библиотека Boost Контейнер указателя. С boost::ptr_vector<T> вы можете хранить список объектов, размещенных в куче, которые могут быть даже полиморфными (виртуальные функции), что невозможно с просто std::vector<T>.

В отличие от std::vector<T>, объекты не будут храниться в подпоследовательных адресах памяти. Однако такие вещи, как изменение размера контейнера, будут выполняться быстрее, потому что элементы сохранят свои исходные адреса в памяти. Лучшим бонусом является то, что вам не нужно вызывать delete самостоятельно: содержащиеся в нем объекты будут уничтожены, когда ptr_vector выйдет за пределы области видимости. Пример:

#include <boost/ptr_vector.hpp>
#include <iostream>
class test() {
protected:
    int const i;
public:
    explicit test(int i) : i(i) {}
    virtual void who_am_i() const { std::cout << "I am test " << i << std::endl; }
};
class special_test : public test {
public:
    explicit special_test(int i) : test(i) {}
    virtual void who_am_i() const { std::cout << "I am special_test " << i << std::endl; }
};
int main() {
    boost::ptr_vector<test> objs;
    for (int i=0; i<50; ++i)
        objs.push_back(new test(i)); // NB: constructing to heap here!
    objs.push_back(new special_test(123)); // objs can also hold inherited classes
    objs[13].who_am_i(); // outputs: I am test 13
    objs[50].who_am_i(); // outputs: I am special_test 123
} // all created objects are automatically destroyed here

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

#define PRINTT(z, n, initializer) initializer
test objs[50] = {
    BOOST_PP_ENUM(50, PRINTT, 1) // yields 1, 1, 1, .... 1
};
#undef PRINTT

Это инициализирует все 50 элементов с 1. boost::pp используется для автоматической печати 1 50 раз подряд.

ARM (Справочное руководство по C++ с аннотациями) (насколько я знаю, устаревшее) оговаривает, что вы не можете выделить массив объектов из класса, в котором отсутствует конструктор по умолчанию. Ваше решение очень умное. (+1 конечно.) (Продолжение ниже.)

Mr.Ree 18.01.2009 04:26

НО: вы автоматически выделяете набор временных объектов, автоматически созданных (продвигаемых) из предоставленных целых чисел, а затем (в зависимости от реализации компилятора) их копируете. Это работает только в том случае, если класс «test» поддерживает общедоступный (или по умолчанию) конструктор копирования.

Mr.Ree 18.01.2009 04:27

да это правда. и в том, как я это сделал, также требуется, чтобы конструктор, принимающий int, не был явным. что ж, вы можете сделать: test obj (1); и используйте BOOST_PP_ENUM (50, PRINTT, obj). но конструктор копирования все еще используется. C++ 1x позволит перемещать вещи в элементы. тогда копия больше не требуется.

Johannes Schaub - litb 18.01.2009 05:48

Другие вопросы по теме