Временный массив в параметрах конструктора

Сразу скажу - заголовок неправильный. Скажите правильный - переименую.

Итак, имеем класс, который принимает два параметра - ссылку на массив и размер массива. Соответственно его конструктор выглядит так:

Axa(byte par_count, byte* par_list);

Я хочу, чтобы в случае, когда массив состоит всего из двух элементов, его можно было вызывать так:

Axa(byte p1, byte p2);

Удобно было бы делать это через делегирующий конструктор:

Axa(byte par_count, byte* par_list);               // основной конструктор
Axa(byte p1, byte p2) : Axa(2,  /* массив {p1, p2} */ );

Собственно вопрос - как правильно сгенерить массив из параметров p1 и p2 прямо при вызове конструктора?

Сперва я хотел сделать так, вроде когда-то так работало:

  Axa(byte p1, byte p2) : Axa(2, (uint8_t[2]) {p1, p2});

Но оказалось, что это был синтаксис С, а в С++ так нельзя. Пишет ошибку “взятие адреса временного массива”.

В итоге я придумал костыль - создаю в этом месте динамический массив через new. Это работает - но не понятно, правильно ли это? И в какой момент я должен вызывать delete для этого массива?
Полный код:

class Axa
{
  public:
  Axa(byte par_count, byte* par_list): count(par_count), list (par_list) {}
  Axa(byte p1, byte p2) : Axa(2, new uint8_t[2]{p1, p2}) {}
  
  const uint8_t count =0;
  uint8_t* list;
  
  uint16_t summ() {
    uint16_t res =0;
    for (byte i = 0; i < count; i++) res += list[i];
    return res;
  }
};

uint8_t a[4] = {1,2,3,4};
Axa Axa1(4,a);
Axa Axa2(2,4);
void setup() {
  // put your setup code here, to run once:
 Serial.begin(9600);
 Serial.println(Axa1.summ());
 Serial.println(Axa2.summ());
}

void loop() {
  // put your main code here, to run repeatedly:

}

Печатает 10 и 6 - правильный ответ.

Думаю, @rkit знает.

а нигде ты его не вызовешь, это временная ссылка, которая после уничтожения класса превратится в висячую, если ты в деструкторе вручную list не уничтожаешь. А вручную его уничтожать как бы и нельзя вот так, сходу. Но Петрович в таких дебрях лучше разбирается, падаждём.

Господи, сколько мучений ради того, что бы не дай бог не напечатать где-то три лишних символа.

Мне кажется, прежде, чем писать на С/С++, нужно внятно сформулировать по-русски, как это должно работать.
По сути - это два существенно различающихся случая:
В первом существует внешний массив, на который будет ссылаться поле объекта. И этот внешний массив, вроде как, должен иметь время жизни больше, чем у объекта. Т.е. если массив создать, заполнить, сформировать объект, удалить массив, то после этого нормальной работы объекта ожидать не следует.
Во втором массива нет, следовательно, его нужно создать (в частности, выделить под него память). При этом массив, опять же, должен существовать все время жизни объекта. По идее массив должен уничтожаться вместе с объектом дабы не утекала память.
Но если массив из первого случая, удалять его при уничтожении объекта нельзя.
Вот тут и следует сначала сформулировать по-русски, как эта вся конструкция должна себя вести. А когда будет по-русски - только тогда переводить на С++.
Со своей стороны вижу два варианта:

  • особый случай count==2. Тогда создается новый массив (даже в первом случае), а по окончании времени жизни объекта он уничтожается. При count!=2 копируется только адрес и уничтожения в деструкторе не происходит.
  • вместо простого массива используется отдельный класс, который и отслеживает, нужно ли освобождать память при завершении жизни экземпляра основного класса. Ну и вместе с тем следит, чтобы пока живет основной объект, нельзя было снаружи уничтожить этот массив (т.е. чтобы избежать неприятностей, описанных в конце 3-го абзаца).

Можно, конечно, разобраться явным образом с обоими случаями

Axa(byte par_count, byte* par_list): count(par_count) {
  if (par_list != NULL) {
     list = new byte[count];
     memcpy(list, par_list, count);
  }
}
Axa(byte p1, byte p2) : Axa(2, (byte*) NULL) {
  list = new byte[2]{p1, p2};
}

создав свой внутренний массив и скопировав туда внешний. Тогда мы можем спосокйно вызывать delete в деструкторе.

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

Мне интересно, существует ли способ правильного создания массива из отдельных переменных прямо в параметрах или что-то типа этого.

красиво… это же лямбда? Я вчера пробовал через лямбды, но что-то статический массив не пришел в голову.

а почему не делегирование?
Я выбрал делегирование, чтобы сохранить совместимость со старой версией конструктора, но не плодить дублирующего кода.
Понятно, что в реальном проекте класс и конструктор значительно сложнее, представленное выше - лишь “минимально-репрезентативный пример”

и это тоже покажите, если не сложно :slight_smile:

Парни, прощу прощения, я там лопухнулся. Так нельзя, сейчас скрою ответ. Я тут немного “не дома”.

Сейчас переделаю.

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

template <uint8_t ASize> 
	TAnalogKeys(const TAnalogKeysStruct (&AKeyMatrix)[ASize], const uint8_t APin)
		: TAnalogSensor::TAnalogSensor(APin, true), FKeyArray(AKeyMatrix), FKeySize(ASize)
	{
		FGap = 5;
		FReadInterval = 125;
	}

AKeyMatrix это массив типа
TAnalogKeysStruct KEY_SHIELD[] = {
{503,key_Left},
{15,key_Right},
{153,key_Up},
{332,key_Down},
{738,key_Set}
};

тебе даже самому размер массива указываь не надо, какой передашь, он такой и схавает

вызывается потом так: FKeys = new TAnalogKeys(KEY_SHIELD, A0);

могу весь текст класса скинуть, мошт почерпнешь полезного

Статический массив нельзя, т.к. он будет “один на все экземпляры”. Возвращаемся к идее динамического. Тут надо понять время его жизни. Поскольку к экземпляру Axa могут обратиться в любой момент, массив должен жить до тех пор, пока живёт экземпляр Axa и освобождаться в деструкторое. Тогда получается

что-то вроде:
class Axa {
  public:
	Axa(byte par_count, byte* par_list): count(par_count), list (par_list) {}
	Axa(byte p1, byte p2) : Axa(2, local = new byte[2] { p1, p2}) {}
	
	const uint8_t count =0;
	uint8_t* list;
	byte *local = nullptr;
	
	~Axa(void) { delete local; }
	
	uint16_t summ() {
		uint16_t res =0;
		for (byte i = 0; i < count; i++) res += list[i];
		return res;
	}
};

uint8_t a[4] = {1,2,3,4};
Axa Axa1(4,a);
Axa Axa2(2,4);
Axa Axa3(3,6);
void setup() {
  // put your setup code here, to run once:
 Serial.begin(9600);
 Serial.println(Axa1.summ());
 Serial.println(Axa2.summ());
 Serial.println(Axa3.summ());
}

void loop() {
  // put your main code here, to run repeatedly:

}

В деструкторе нет никакой проверки запрашивалась ли память (она же могла и не запрашиваться, если первый из конструкторов сработал), т.к. delete адекватно работает с nullptr.

3 лайка

спасибо!

Если честно не понял.
Один на все - это значит “впервые инициализированный” на все последующие? Или одна область памяти и все последующие запихают свои данные в “чужой огород”?

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

именно так

Дорогой! Присоединюсь к голосам тех, кто не согласен с твоим “удобно”. Нет, не удобно. Что и показано в ветке выше. Всегда возникнет необходимость следить за освобождением памяти.

Удобно писать конструктор с шаблонами для массива. Ну и конечно в С++ не нужно размер массива передавать. И альтернативный конструктор с переменным числом аргументов, если массив задать перечислением.

Если же задача именно в “скилл ап” по делегированию - то тут другое дело. тогда всё ОК.

это почему же не нужно, да еще и “конечно”? :slight_smile:

удобно - это вопрос субьективный :slight_smile: Мне - удобно.

а вот это как раз неудобно, если у меня не “массив вообще”, а только два варианта - с двумя и шестью элементами. В этом случае мне проще написать виртуальный базовый класс и унаследовать от него классы для обоих массивов, чем связываться с шаблонами.
Шаблоны на самом деле очень негибкий инструмент.С их частичной специализацией мне или придется писать кучу одинакового кода для двух вариантов, либо городить промежуточный уровень родительских классов.

Ну я же еще на старом форуме показывал это пример. Шаблон по размеру пишется.
Мне неловко, но это ж из учебника пример!

а, понятно. Спасибо. С шаблонами я умею.
Но не хочу :slight_smile: