Как лучше реализовать конструкторы в структуре больших чисел?
Пишу структуру больших чисел. Остановился на написании конструкторов.
Вот пример как я их реализовал:
#include "big_uint.h"
big_uint::big_uint(){
size = 0;
buff_size = 4;
bits = new uint32_t[buff_size];
}
big_uint::big_uint(uint64_t value) : big_uint() {
bits[0] = (uint32_t)value;
bits[1] = (uint32_t)(value >> 32);
size += (bool)bits[1] + 1;
}
big_uint::big_uint(uint32_t value) : big_uint() {
size = 1;
bits[0] = value;
}
big_uint::big_uint(uint16_t value) : big_uint((uint32_t)value) {}
big_uint::big_uint(int64_t value) : big_uint((uint64_t)value) {}
big_uint::big_uint(int32_t value) : big_uint((uint32_t)value) {}
big_uint::big_uint(int16_t value) : big_uint((uint32_t)value) {}
big_uint::big_uint(const uint32_t* const bits, uint32_t size){
this->size = size;
this->buff_size = size + 4;
this->bits = new uint32_t[this->buff_size];
const uint32_t *s = bits;
for(uint32_t* f = this->bits; s < bits + size; *f = *s, ++f, ++s);
}
big_uint::big_uint(const big_uint& other) : big_uint(other.bits, other.size) {}
big_uint::big_uint(big_uint&& other) {
this->size = other.size;
this->buff_size = other.buff_size;
this->bits = other.bits;
other.bits = nullptr;
}
И вопрос в том, как их реализовать лучше как у меня или каждый конструктор по своему? То есть в конструкторе uint32_t писать size = 1; bits[0] = value;, и такой же код в uint16_t.
Мне показалось, что это лучше чем писать в каждом конструкторе практически один и тот же код.
Ответы (1 шт):
Хотите супер контроля над своим кодом - пишите каждый конкретный конструктор так как нужно. Похоже это правильный способ в Вашем случае - конструкторы будут немного отличатся.
Но если Вы считаете, что у Вас содержимое их похожее/одинаковое - используйте делегирующий конструктор. Собственно Вы это и сделали. Правда я не уверен, что там все правильно - типы разные в констукторах и есть сразу несколько конструкторов с одинаковой сигнатурой.
Посмотрим, что ещё можно сделать.
В некоторых случаях можно написать макрос, который просто размножить
#define ctor(type) big_uint::big_uint(type value) : big_uint((uint64_t)value) {}
// потом где то так
ctor(uint32)
ctor(uint16)
Но сейчас макросы не в почете. Сейчас модно шаблоны:) Сделаем на шаблонах.
class big_int {
public:
template<typename T>
big_int(T value) { m_value = value;}
private:
uint64_t m_value;
};
и теперь можно вот так
big_int a{1u};
big_int b{100};
big_int c{100lu};
Правда конструктор по умолчанию теперь не будет автоматом генерироваться. Так что его придется дописать явно. Также не забывайте, что определив конструкторы копирования/перемещения, неплохо написать и операторы копирования/перемещения (правило трех/пяти)
Посмотрел на конструктор перемещения. Рекомендуется в них объект-донор оставить "пустым", то есть, не забыть занулить переменные. Где то так
big_uint::big_uint(big_uint&& other) {
this->size = other.size;
this->buff_size = other.buff_size;
this->bits = other.bits;
other.bits = nullptr;
other.size = 0;
other.buff_size = 0;
}
зачем? после перемещения объект должен быть готов к удалению. Я не знаю, как именно устроен деструктор, но с занулением будет надежнее.
И не забывайте, что перед таким конструктром неплохо написать explicit, что бы случайно не написать big_int a = 2;, хотя может оно и подходит в данном случае.