LINUX.ORG.RU

Указатель this когда становится доступен (с++)

 


0

3

Добрый день, подскажите, когда становится в классе доступен указатель this. Допустим хочу передать в член класса, указатель на родительский объект при инициализации. Можно ли это сделать в конструкторе объекта?

class B {
public:
    B(A *a) {
        mA = a;
    }
A* mA;
}
class A {
public:
    A() :mB(this) {}
B *mB;
}

}


Сразу доступен. А вот зачем ты указателю на класс B передаёшь указатель на экземпляр класса A, непонятно

XMs ★★★★★
()

Доступен сразу же, конечно, но вот пользоваться стоит аккуратно. Если передавать this куда-то в списке инициализации, то надо помнить, в каком порядке инициализируются базовые классы и поля, чтобы инициализатор поля, которому ты передашь this, не прочитал через него что-то, что ещё не проинициализировано.

ilammy ★★★
()
Ответ на: комментарий от da17

Место на стеке/в куче размером sizeof(имякласса) выделяется сразу, т.е. указатель доступен. До инициализации, конечно, по этому адресу будет мусор, но сам адрес корректен.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от XMs

Сразу доступен. А вот зачем ты указателю на класс B передаёшь указатель на экземпляр класса A, непонятно

Потому что классы A и B выполняют одну функцию, но в книжке написано, что нужно разбить на несколько классов — так и делаем.

byko3y ★★★★
()

указатель это всего лишь адрес. как только выяснили где лежит класс и выделили память - у тебя есть адрес. этот адрес и есть this. нюансы могут быть только при вызове функций с виртуальным наследованием по указателю на функцию. вот там да, сложно.

а всё потому, что надо сперва на асме попрограмиировать. понять как оно «в железе». тогда такие вопросы просто не появляются.

salozar
()
Ответ на: комментарий от salozar

действительно. вот растамане вообще кодают не приходя в сознание и ничего им помнить не надо.

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

anonymous
()
Ответ на: комментарий от in_dance

да, можешь начинать использовать

anonymous
()

На всякий случай: в C++ ты не сможешь вызвать в конструкторе виртуальный метод!

anonymous
()
Ответ на: комментарий от PRN

Хорошо спроектированный и реализованный язык, сводит эти «надо помнить» к минимуму. А С++ убогая пародия, на нормальный инструмент.

anonymous
()
Ответ на: комментарий от in_dance

Это нигде не работает. Там должно быть (x != 0) | x >> 31. Вот это будет работать на большинстве платформ с 32-х битным интом. Битовый сдвиг вправо числа со знаком - это implementation defined операция.

Кстати, до C++14 компилятор имеет право заменить return x << 30 >> 30; на return x;, так как единственные значения x не вызывающие знакового переполнения - это -1, 0 и 1. И знание ассемблера тут не поможет. Нужно знать язык.

red75prim ★★★
()
Ответ на: комментарий от anonymous

ты не сможешь

сможешь. только результат будет отличаться от ожидаемого. ы

anonymous
()
Ответ на: комментарий от red75prim

И знание ассемблера тут не поможет. Нужно знать язык.

нельзя так просто взять и узнать с++.jpg

anonymous
()
Ответ на: комментарий от da17

Т.е. я еще не вызвал конструктор класса А, а this уже существует?

Когда ты «в списке инициализации», конструктор уже вызван.

utf8nowhere ★★★
()
Ответ на: комментарий от utf8nowhere

А что значит «конструктор вызыван», я понимаю, что «конструктор вызыван», значит память выделилась под объект и началась инициализация членов классов, которая закончена к моменту вызова тела конструктора и начало выполняться тело конструктора. Что включает в себя термин «конструктор вызван»?

da17
() автор топика
Последнее исправление: da17 (всего исправлений: 1)
Ответ на: комментарий от da17

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

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

anonymous
()
Ответ на: комментарий от da17

Инициализируются (то есть вызываются для них конструкторы), но сам объект валидным может считаться только после отработки (тела) конструктора. Но указатель на этот объект ты можешь давать другим объектам хоть в списке, хоть в теле, но тут уже что с этим указателем там можно и нельзя делать - на твоих плечах.

anonymous
()
Ответ на: комментарий от anonymous

я так понимаю, я могу с ним все делать в других объектах, кроме вызова виртуальных ф-ий

da17
() автор топика
Ответ на: комментарий от da17

Что включает в себя термин «конструктор вызван»?

Формально, думаю, не определено. Я бы сказал что control flow переместился в function-body (ХЗ ваще насколько (без) грамотно мешать синтаксис с выполнением). Инициализаторы это часть function-body.

utf8nowhere ★★★
()
Ответ на: комментарий от anonymous

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

Члены класса могут считаться проинициализироваными после того, как они были проинициализированы в списке инициализации (в т.ч. неявно).

utf8nowhere ★★★
()
Ответ на: комментарий от utf8nowhere

И да и нет, тут небольшая путаница с терминологией. Обычно тело конструктора как-то меняет члены класса, что по-сути и должно считаться их реальной инициализацией.

anonymous
()

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

в добавок этот дарес не очень то точный :)
(то есть если занесешь его в какой-то реестр, потом можешь пожать странные результаты)

#include <iostream>

class base_1 {
public:
    base_1() { std::cout << this << std::endl; }
    virtual ~base_1() noexcept =default ; 
    virtual void foo() noexcept {}
};
class base_2 {
public:
    base_2() { std::cout << this << std::endl; }
    virtual ~base_2() noexcept =default ;
};
class child : public base_1, public base_2 {
public:
    child() { std::cout << this << std::endl; }
};

int main(int,char**)
{
    child a;
    return 0;
}

ps: почему спойлер не показывается, что не так делаю?

zerhud
()
Последнее исправление: zerhud (всего исправлений: 1)
Ответ на: комментарий от zerhud

выделить память != создать

Да. Но и успешное завершение работы конструктора ≠ создание.

utf8nowhere ★★★
()
Ответ на: комментарий от salozar

Нагружать свою память всеми UB и указательной магией, вместо того, чтобы сосредоточиться на логике кода, а проверку корректности оставить компилятору, это так здорово!

meliafaro ★★★★★
()
Ответ на: комментарий от zerhud

выделить память != создать

Я тут поторопился соглашаться. Иногда выделить память == создать объекты. См. Some operations are described as implicitly creating objects within a specified region of storage. и These functions [aligned_alloc, calloc, malloc, reallocприм. от меня] implicitly create objects in the returned region of storage and return a pointer to a suitable created object.

utf8nowhere ★★★
()
Последнее исправление: utf8nowhere (всего исправлений: 1)
Ответ на: комментарий от utf8nowhere

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

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

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

в цитате говорится о другом. да, память уже есть для обекта. если это int то выделения памяти достаточно, чтобы объект был создан. и вобше си - отдельная тема. нет там конструкторов.

zerhud
()
Ответ на: комментарий от zerhud

если говорить о c++ и конструкторах, то когда конструктор завершается без ошибок, то объект считается созданным.

An object is created by a definition, by a new-expression, by an operation that implicitly creates objects (see below), when implicitly changing the active member of a union, or when a temporary object is created. Не вижу тут про «когда конструктор завершается без ошибок». :/

и вобше си - отдельная тема. нет там конструкторов.

Цитата из стандарта C++.

utf8nowhere ★★★
()
Ответ на: комментарий от utf8nowhere

что-то мне сходу не найти. но вот код

class ctor_test {
public:
ctor_test() { }
ctor_test(int) { throw 1; }
~ctor_test() { std::cout << "dtor" << std::endl; }
}; 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               int main(int,char**)
{                                                                                                                                                                                                                                                    
    try {                                                                                                                                                                                                                                                
        ctor_test a;                                                                                                                                                                                                                         
    } catch(...) {}
    try { 
        ctor_test a(1);
    } catch(...) {} 
    return 0;
}

сколько раз появится надпись dtor?

zerhud
()
Ответ на: комментарий от meliafaro

Полагаю, в любом ЯП с подобной парадигмой есть тонкое место с конструированием объекта. В Java, например, очень не рекомендуется передавать this наружу до окончания работы конструктора, потому что потенциально его может поюзать другой поток, и тогда состояние гонки и совсем всё плохо.

izzholtik ★★★
()
Ответ на: комментарий от utf8nowhere

Ну да. А конструктор вызывался дважды. При этом второй раз он не до работал до конца и деструктор вызван не был.

Это важное свойство. Например в конструкторе файл открывается, а в деструкторе закрывается, системными вызовами. Если файл открыть не получилось то и закрывать не нужно.

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

zerhud
()
Ответ на: комментарий от izzholtik

Ну, например, в Rust в качестве конструктора обычно используют ассоциативные функции (= static функции в C++), поэтому никакого this там нет, такая функция и возвращает объект.

meliafaro ★★★★★
()
Ответ на: комментарий от in_dance

Значения просто складываются в структуру. return Foo{ a: 1, b: 2 };. Инкапсуляция обеспечивается контролем доступа на уровне модулей.

red75prim ★★★
()
Ответ на: комментарий от in_dance

Создаются все необходимые переменные, с помощью которых создаётся объект как возвращаемое значение.

pub fn new() -> MidiIO {
        let it = NoteIter::new();
        let notes: HashMap<String, u8> = it
                                        .zip(0..128)
                                        .collect();

        let input = MidiInput::new("Generic MIDI input").unwrap();
        let ports = input.ports();
        let mut ports = Vec::<String>::new();
        for i in &input.ports() {
            ports.push(input.port_name(i).unwrap());
        }

        let output = MidiOutput::new("Generic MIDI output").unwrap();
        let out_ports = output.ports();
        let mut o_ports = Vec::<String>::new();
        for i in &output.ports() {
            o_ports.push(output.port_name(i).unwrap());
        }

        MidiIO {
            input: input,
            inputs: ports,
            current_in: String::new(),
            output: output,
            outputs: o_ports,
            current_out: String::new(),
            notes: notes
        }
}

Последнее выражение возвращает значение из функции, return можно опустить.

meliafaro ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.