C++ Неявно вызывается лишний конструктор. Классы как поля другого класса

223
17 марта 2018, 17:54

Есть простенькая программа на с++. Есть три класса, один содержит приватными полями 2 других. При вызове конструктора первого неявно вызываются конструкторы остальных. Вопрос почему? и что более важно, как этого избежать?

    class Device
        {
        private:
            Date date;
            TypeSensor type;
            int placeBinding;
            Sensor sensor;
......
    }

То есть при вызове конструктора девайс вызывается конструктор Date. Все бы ничего, но TypeSensor нужно инициализироват ранее, нежели остальные поля. Поэтому интересно есть ли способы решения этой проблемы.

enum TypeSensor {
    ACCELEROMETER,
    PROXYMETER,
    TEMPERATURE
};
class Sensor
{
    string unitOfMeasured;
    int maximum;
    int minimum;
    float value;
.....
}
Answer 1

Порядок инициализации переменных определяется их следованием в объявлении класса. Переставьте в объявлении класса TypeSensor и Date, что бы TypeSensor инициализировался первым.

class Device
    {
    private:
        TypeSensor type;
        Date date;
        int placeBinding;
        Sensor sensor;
}

Непонятно чего вы хотите избежать. При создании объекта, обязаны инициализироваться все поля класса. Если в объявлении класса стоит Date date; то это поле обязано проинициализироваться при создании Device.

Однако если вы хотите избежать вызова конструктора Date то можно сделать указатель на него Date* date; и выделить память в другом месте.

Answer 2

В языке С++ конструктор объекта класса всегда вызывает конструкторы всех его подобъектов. Так работает язык С++. Избежать этого нельзя.

Порядок инициализации подобъектов определятся видом подобъекта (базовый подобъект или подобъект-член) и порядком объявления подобъекта среди аналогичных подобъектов. В вашем случае требуемого вам порядка инициализации можно достичь просто переупорядочиванием объявлений членов класса.

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

  • Можно сделать конструктор по умолчанию подобъекта ничего не делающим, а для фактической инициализации предоставить специальный метод.

  • Можно сделать агрегацию подобъектов не напрямую, а через умный указатель std::unique_ptr.

  • Вместо агрегации объектов напрямую можно агрегировать std::optional.

И т.д.

Answer 3

Никак не связано с вопросом, но у Вас SensorType вынесен из класса Sensor. Может быть есть смысл сделать SensorType частью класса Sensor и передавать его в конструктор? Как-нибудь так, например:

class Sensor
{
public:
    enum class Type{
        DEFAULT = 0,
        ACCELEROMETER = 0,
        PROXYMETER = 1,
        TEMPERATURE = 2
    };
    explicit Sensor(const Type& type = Type::DEFAULT):
        m_unitOfMeasured{},
        m_maximum{1},
        m_minimum{0},
        m_value{0.0},
        m_type{type}
    {
        switch (m_type) {
        case Type::ACCELEROMETER:
            m_unitOfMeasured = "acceleration";
            ...
            break;
        case Type::PROXYMETER:
            m_unitOfMeasured = "level";
            ...
            break;
        case Type::TEMPERATURE:
            m_unitOfMeasured = "degree";
            ...
            break;
        default:
            ...
            break;
        }
    }
private:
    std::string m_unitOfMeasured;
    int m_maximum;
    int m_minimum;
    float m_value;
    Type m_type;
};
class Date{};
class Device
{
public:
    Device(const Sensor::Type& sensorType = Sensor::Type::DEFAULT):
        m_sensor{sensorType},
        m_date{},
        m_placeBinding{}
    {}
private:
    Sensor m_sensor;
    Date m_date;
    int m_placeBinding;

};

UPD. Ну тогда вот так как-то:

enum class SensorType{
    DEFAULT = 0,
    UNKNOW = 0,
    ACCELEROMETER = 1,
    PROXYMETER = 2,
    TEMPERATURE = 3
};

class Sensor
{
public:
    Sensor(const SensorType& type = SensorType::DEFAULT):
        m_unitOfMeasured{},
        m_maximum{1},
        m_minimum{0},
        m_value{0.0},
        m_type{type}
    {
        switch (m_type) {
        case SensorType::ACCELEROMETER:
            m_unitOfMeasured = "acceleration";
            break;
        case SensorType::PROXYMETER:
            m_unitOfMeasured = "level";
            break;
        case SensorType::TEMPERATURE:
            m_unitOfMeasured = "degree";
            break;
        default:
            break;
        }
    }
private:
    std::string m_unitOfMeasured;
    int m_maximum;
    int m_minimum;
    float m_value;
    SensorType m_type;
};
class Date{};
class Device
{
public:
    Device(const Date& date, const int placeBinding, const SensorType& sensorType= SensorType::DEFAULT):
        m_type{sensorType},
        m_sensor{sensorType},
        m_date{date},
        m_placeBinding{placeBinding}
    {}
    Device(const Device& other):
        m_type{other.m_type},
        m_sensor{other.m_sensor},
        m_date{other.m_date},
        m_placeBinding{other.m_placeBinding}
    {
    }
    static Device createByUser(){
        int n;
        std::cout << "choise 1, 2 or 3 type of sensor: " << std::endl;
        std::cout << "1. ACCELEROMETER" << std::endl;
        std::cout << "2. PROXYMETER" << std::endl;
        std::cout << "3. TEMPERATURE" << std::endl;
        std::cin >> n;
        if (n >= 1 && n <= 3){
            return Device(Date(), 0, static_cast<SensorType>(n));
        }else{
            return Device(Date(), 0);
        }
    }
private:
    SensorType m_type;
    Sensor m_sensor;
    Date m_date;
    int m_placeBinding;
};
READ ALSO
Datagram сокет с поддержкой overlapped I/O operations

Datagram сокет с поддержкой overlapped I/O operations

При создании сокета можно указать WSA_FLAG_OVERLAPPED, что это означает? В MSDN достаточно скудная информацияЧто это вообще за технология overlapped I/O operations...

209
Удаляется DLL из SysWOW64 после перезагрузки

Удаляется DLL из SysWOW64 после перезагрузки

Пишу инсталятор на InnoSetup для своей программы

194
добавить картинку в textView

добавить картинку в textView

Доброго времени суток! Как можно добавить картинку в текст, что бы было такое

175
Можно, ли перевести String в код java

Можно, ли перевести String в код java

В Python есть метод exec(), есть ли такое в Java?

189