Страницы

Поиск по вопросам

понедельник, 23 декабря 2019 г.

Возможно ли присвоить результат new не указателю?

#cpp #указатели #конструктор #преобразование


Насколько мне известно, результат выполнения new нужно присваивать указателю, т.е.:

T t = new T(); //должна быть ошибка. Несоответствие типов т.к. new возвращает указатель
T *t = new T(); // Правильный вариант


Однако, необъяснимым для меня образом, следующий код делает первый вариант возможным.
Причем только для одного конечного класса C1. Если попытаться сделать подобное с другим
C2, появляется ошибка. Причем если упростить конструктор (убрать список инициализации
и аргументы), то все будет работать по-обычному.

#include 

class A
{
private:
    int id;
    static int instrumentsCount;
    static int lastId;
public:
    A();
    virtual ~A() = 0;
};
class B:public A
{
private:
    int field1;
public:
    B(int arg):A(),field1(arg) {std::cout<<"B\n";}
};
class C1:public B
{
private:
    const bool field2;
public:
    C1(bool o = true):B(170),field2(o){std::cout<<"C1\n";}
    ~C1();
};
class C2:public B
{
private:
    const int field3;
public:
    C2(int d = 20):B(120),field3(d){std::cout<<"C2\n";}
    ~C2();
};
int A::instrumentsCount = 0;
int A::lastId = 0;
A::A()
{
    this->id = A::lastId++;
    A::instrumentsCount++;
    std::cout<<"A "<id<<" created\n";
}
A::~A()
{
    std::cout<<"A "<id<<" destroyed\n";
    A::instrumentsCount--;
}
C1::~C1(){}
C2::~C2(){}

int main(int argc, char *argv[])
{
    C1 c1 = new C1(true);
    C2 *c2 = new C2(10);
    return 0;
}


Как результат выводится следующее: 

A 0 created
B
C1
A 1 created
B
C1
A 2 created
B
C2
A 1 destroyed


Что в принципе логично, но почему конструктор базового класса A вызывается при создании
C1 два раза? Прошу открыть мне глаза на мои ошибки( Использую QT Creator 5.9
    


Ответы

Ответ 1



Имеется фундаментальный тип, для которого вы можете написать выражение T t = new T(); Таким типом является фундаментальный тип bool. bool b = new bool(); Если инициализатор отличен от нуля, то переменная получает значение true , в противном случае значение false. Из стандарта C++ (4.14 Boolean conversions) 1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool. A zero value, null pointer value, or null member pointer value is converted to false; any other value is converted to true. For direct-initialization (8.6), a prvalue of type std::nullptr_t can be converted to a prvalue of type bool; the resulting value is false. Однако такой код ведет к утечке памяти, так как значение указателя на выделенную память теряется. В примере кода из вашего вопроса в классе C1 имеется конструктор преобразования C1(bool o = true):B(170),field2(o){std::cout<<"C1\n";} Параметр этого класса имеет тип bool, а переданный в качестве аргумента указатель может неявно быть преобразован в тип bool.

Ответ 2



Да все не просто просто, а очень просто: C1 c1 = new C1(true); Итак, создается новый C1, указатель на который используется как инициализатор для конструирования c1. Есть конструктор C1(bool), который и использован. Имеем - созданный в динамической памяти C1, потерянный (утечка памяти), так как ненулевое значение указателя просто неявно преобразовано в bool для вызова второго конструктора - конструктора, который создает c1... Что логично - вы же создаете два объекта C1.

Ответ 3



Моё дополнение не совсем по теме, но может быть кому-то полезно. Результат, возвращённый оператором new (или функцией malloc) можно присвоить целому числу, но нужно использовать приведение типов: uint32_t a = (uint32_t)malloc(5); Тогда в переменную a попадёт адрес участка памяти, выраженный целым числом. В рядовых программах так делать не рекомендуется, но если вы работаете напрямую с железом, это может быть полезно. Например, это число можно будет записать в регистр контроллера DMA.

Комментариев нет:

Отправить комментарий