Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.82/34: Рейтинг темы: голосов - 34, средняя оценка - 4.82
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
1

Инициализация полей класса при наследовании

13.03.2017, 23:54. Просмотров 6136. Ответов 38
Метки нет (Все метки)


Всем добрый вечер.
Возникла у меня небольшая задачка, которую никак не могу решить.
Имеем базовый класс с константным полем:
C++
1
2
3
4
5
6
class A{
public:
    A() {}
protected:
    const int   field;
};
Имеем наследника :
C++
1
2
3
4
5
class B: public A{
public:
    B() {}
    
};
Задача. При создании экземпляра наследника, необходимо проиницилизировать поле field.
Пытаюсь сделать так :
C++
1
2
3
4
5
class B: public A{
public:
    B(): field(1000) {}
    
};
, но не выходит каменный цветочек. Компилятор (g++) на меня сильно материться. Мол нет в классе 'B' поля 'field' :
error: class 'B' does not have any field named 'field'
Хотя, если поле не константа, то в конструкторе класса 'B' я свободно пишу
C++
1
field=1000;
и компилятор это одобряет.

Люди, скажите, пожалуйста, что ему не нравится ? И как можно проинициализировать константное поле в наследнике ?

Спасибо.
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.03.2017, 23:54
Ответы с готовыми решениями:

Инициализация при множественном наследовании
Имеется иерархия типов. Во главе: базовый класс-интерфейс, далее один базовый класс, от него два...

Инициализация полей класса
Приветствую всех. Не в одной книге по С++ сказано, что поля класса желательно инициализировать в...

Инициализация полей во время объявления класса
Инициализировать поля класса во время его объявления возможно только если в классе отсутствует...

Инициализация полей класса через vector<>
Здравствуйте, можете подсказать как правильно инициализировать поля класса, вот например есть класс...

__________________
Помогаю в написании курсовых работ и дипломов здесь.
Записывайтесь на профессиональные курсы C++ разработчиков
38
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 00:01 2
Цитата Сообщение от alkl Посмотреть сообщение
Люди, скажите, пожалуйста
возможно вам станет понятнее если вы в классе B создатите переменную с именем field - получается конфликт имёт по вашей логике, и field имя нельзя создавать в классе В

Ничего не смущает, что в С++ у наследников нельзя создавать имена переменных как у класса родителя?!?!


А теперь по делу!
Родитель создается в момент вызова конструктора А, он у вас вызывается неявно до вызова инициализации переменной field которой нету в В

Таблетка:

C++
1
B() : A(1255)
где у класса А есть конструктор инициализирующий свои поля
2
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 00:12  [ТС] 3
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
возможно вам станет понятнее если вы в классе B создатите переменную с именем field - получается конфликт имёт по вашей логике, и field имя нельзя создавать в классе В
Ничего не смущает, что в С++ у наследников нельзя создавать имена переменных как у класса родителя?!?!
Я и не создаю копию поля в B-классе, знаю что будет конфликт.

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Родитель создается в момент вызова конструктора А, он у вас вызывается неявно до вызова инициализации переменной field которой нету в В
Тут вообще ничего не понял. Кто и когда вызывается, до какой инициализаци, почему переменной нету, если она есть, ...

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Таблетка:
Сложно, конечно, это назвать "таблеткой", но как вариант - пойдёт. Спасибо.
Всё равно не то, что хотелось бы...
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 00:22 4
Вот еще пример
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include "Some1.h"
using namespace std;
 
class SecretBase
{
protected:
    const int m_code = 17;
public:
    SecretBase( const int am )
        : m_code( am )
    {
    }
};
 
class Secret : public SecretBase
{
public:
    Secret() : SecretBase( 55 )
    {
    }
};
 
 
class CustumSecret : public Secret
{
private:
    int m_code;
 
public:
    CustumSecret()
        : m_code( Secret::m_code + 1000)
    {
    }
    int getSecret() const
    {
        return m_code;
    }
};
 
 
int main()
{
    CustumSecret secret;
    cout << secret.getSecret() << endl;
}
Добавлено через 1 минуту
Цитата Сообщение от alkl Посмотреть сообщение
Тут вообще ничего не понял. Кто и когда вызывается, до какой инициализаци, почему переменной нету, если она есть, ...
Да она есть - но она уже создана и должна была быть инициализирована!

Цитата Сообщение от alkl Посмотреть сообщение
Всё равно не то, что хотелось бы...
Учитывая то как вы описали вашу проблему - я с радостью всё вам объясню по 100 раз.

Задайте ряд вопросов - всё разберём!

Добавлено через 4 минуты
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include "Some1.h"
using namespace std;
 
 
class SomeVar
{
public:
    SomeVar( int f )
    {
        cout << "I am some var with " << f << endl;
    }
};
 
class MustBeInited
{
private:
    int m_x;
public:
    MustBeInited()
    {
        cout << "Dont inited" << endl;
    }
    MustBeInited( int x ) 
        : m_x(x)
    {
        cout << "Already inited with " << x << endl;
    }
};
 
 
class A
{
protected:
    const MustBeInited m_some;
};
 
class B : public A
{
private:
    SomeVar m_some;
 
public:
    B() : m_some( 14 )
    {
 
    }
};
 
int main()
{
    {
        B b;
    }
    cout << "Hello world" << endl;
}
Добавлено через 1 минуту
Цитата Сообщение от alkl Посмотреть сообщение
Я и не создаю копию поля в B-классе, знаю что будет конфликт.
конфликт разрешаемый при явном указании к какому m_var мы обращаемся - я не зря там указал фразу "по вашей логике"
1
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 00:24  [ТС] 5
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Вот еще пример
Ну, собственно, это то, что Вы раньше назвали "таблеткой". Это я уже понял...

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Да она есть - но она уже создана
Не понятно, почему она уже создана (ещё до создания экземпляра класса 'B'). Ведь, по сути, класса 'A', фактически не существует, и при создании экземпляра класса 'B', создаётся именно его экземпляр, в котором существуют все переменные, находящиеся в 'A'. Грубо говоря, просто копируются. Тогда не понятно, почему её нет в 'B'.
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 00:28 6
Цитата Сообщение от alkl Посмотреть сообщение
Не понятно, почему она уже создана
Пример с MustBeInited() - он вызывается до SomeVar( int f )

Цитата Сообщение от alkl Посмотреть сообщение
Ведь, по сути, класса 'A', фактически не существует, и при создании экземпляра класса 'B'
Это не верно. Фактическим называется то, что можно потрогать. Вам привести пример где можно обращаться к полям родителя или его методам? Он есть - но поглащён классом Б как наследник.

Цитата Сообщение от alkl Посмотреть сообщение
Грубо говоря, просто копируются.
Тоже не верно. При "грубом копировании" не вызывался бы конструктор родителя как полноценного объекта.

Цитата Сообщение от alkl Посмотреть сообщение
Тогда не понятно, почему её нет в 'B'.
Класс А есть в классе Б - можно ведь обратиться к полям на чтение и запись!
1
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 00:53  [ТС] 7
Мда... Я начинаю понимать, что и ничего не понимаю...

Попробую описать общую задачу.
Имеем базовый класс. У него есть свой конструктор. Он вызывается раньше всех производных конструкторов - в этом, как раз у меня и проблема. Мне необходимо, создавая наследника, проинициализировать переменную раньше, чем вызовется конструктор базового класса.
Для примера :
C++
1
2
3
4
5
6
7
8
9
10
class A{
public:
    A(){
        var=var_init;   // Тут нужно, чтобы 'var_init' уже была проинициализирована наследником
    }
protected:
    int     var_init;
private:
    int     var;
};
В принципе, поле можно сделать и не константным (как в примере выше), если это как то поможет проблеме ...
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 00:59 8
Цитата Сообщение от alkl Посмотреть сообщение
Мне необходимо, создавая наследника, проинициализировать переменную раньше, чем вызовется конструктор базового класса.
Может меня попровят сторожили, но это невозможно

Если рассуждать от обратного, то наследник, по вашей логике, инициализирует поле родителя. А если он этого не сделает? Она будет в подвешенном состоянии? Ну вот забыл родитель это сделать .... . После вызова конструтора родителя объект, что внутри родителя должен уже быть в полной боевой готовности!

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
protected:
    int _q;
public:
    A()
    {
    }
};
 
class B : public A
{
protected:
    int _t;
public:
    B() : _t(_q) 
        // если тут _q в подвешенном состоянии был бы, то вообще беда бы была!!!!!
        // Но память под _q выделена и он проинициализирован A()
    {
    }
};
Еще пример, а если нету наследника? Получается создавая объект А у него поле "не инициализировано" ? Стандарт языка позаботился, что такого бы не было!
0
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 01:03  [ТС] 9
На этот случай в родителе дефолтное значение :
C++
1
2
3
4
5
6
7
8
9
10
class A{
public:
    A(){
        var=var_init;   // Тут нужно, чтобы 'var_init' уже была проинициализирована наследником
    }
protected:
    int     var_init;
private:
    int     var=0;      // Если наследника нет, то иним нулём
};
Добавлено через 1 минуту
Как раз, таки, мне это и нужно. Если в наследнике не указать значение, то оно будет неким по-умолчанию. Это один из моментов в моей задаче.
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 01:12 10
Цитата Сообщение от alkl Посмотреть сообщение
int * * var=0; * * *// Если наследника нет, то иним нулём
Просто приймите за факт фразу далее: эта запись сахарок записи
C++
1
2
A() : var(0)
{}
Магии не бывает, класс А инициализирует свои переменные в списке инициализации - указаны они там явно или нет

Добавлено через 3 минуты
alkl, нельзя в списки инициализации наследника инициализировать поля родителя, потому что они уже проинициализированы - смотрите еще раз пример с MustBeInited()

Добавлено через 4 минуты
Еще довод:
http://en.cppreference.com/w/c... lizer_list
In the definition of a constructor of a class, member initializer list specifies the initializers for direct and virtual base subobjects and non-static data members.
где тут фраза and non-static subobjects data members ? Нету такой - потому, что это не верно
1
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 01:13  [ТС] 11
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
alkl, нельзя в списки инициализации наследника инициализировать поля родителя, потому что они уже проинициализированы
Странно, что компиль ругается на то, что поля не существует. Скорее, его просто ещё нет до создании экземпляра, и добавляется оно только когда он создаётся.

Будем дальше думать, спасибо...
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 01:15 12
Цитата Сообщение от alkl Посмотреть сообщение
Скорее, его просто ещё нет до создании экземпляра, и добавляется оно только когда он создаётся.
Да есть оно там! Я уже начинаю повторяться))))))) Он ругается, потому что думает что вы забыли создать в Б поле field, а уже стремитесь его проинитить - он же не может писать ошибку : Cannot init base class member - он не подозревает, что вы хотите невозможного
1
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 01:23  [ТС] 13
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
потому что думает что вы забыли создать в Б поле field, а уже стремитесь его проинитить
Ну и я о том. Он в этот момент его не видит и думает, что я забыл его добавить. А после его создания, уже видны все поля родителя, по этому я в его конструкторе спокойно могу обращаться к любому полю родителя.

Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Я уже начинаю повторяться)))))))
Извините, не хотел заставлять Вас это делать

В общем, картина стала ясна. Будем кумекать, как лучше сделать ...
Хочется иметь некоторые переменные по-умолчанию, а при создании наследника, иметь возможность их изменять до вызова базового конструктора. Причём, хотелось бы это делать в виде списка инициализации.
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 01:25 14
Цитата Сообщение от alkl Посмотреть сообщение
Он в этот момент его не видит
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
protected:
    int _q;
public:
    A()
    {
    }
};
 
class B : public A
{
protected:
    int _t;
public:
    B() : _t(_q) 
        // если тут _q в подвешенном состоянии был бы, то вообще беда бы была!!!!!
        // Но память под _q выделена и он проинициализирован A()
    {
    }
};
тогда я маг и волшебник)) Видит он всё ... . А почему он не реагирует при _q(18) описал в посте после фразы Еще довод:. Он не верит своим двоичным сердцем, что вы настолько ошибаетесь, что пытаесь проинить то, что не можете - что можно проинить описано по ссылке http://en.cppreference.com/w/c... lizer_list


Цитата Сообщение от alkl Посмотреть сообщение
В общем, картина стала ясна.
1
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 01:35  [ТС] 15
Значит, по мнению компилятора, в случае
C++
1
    B() : _t(_q)
я не забыл добавить поле _q в 'B', а случае
C++
1
    B() : _q(_t)
забыл.
Картина ещё интереснее разворачивается
0
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 01:46 16
alkl, ага - почитайте ошибки

http://rextester.com/ITI45792 _q(18)
http://rextester.com/KAGWC31525 _t(_q)
0
Игогошка!
1800 / 707 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
14.03.2017, 01:46 17
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Просто приймите за факт фразу далее: эта запись сахарок записи
Говорят, что какая-то разница все-таки есть.
https://github.com/isocpp/CppC... rs-instead
1
2540 / 1199 / 358
Регистрация: 30.11.2013
Сообщений: 3,820
14.03.2017, 01:50 18
ct0r,
The compiler-generated function can be more efficient.
Спорить с вами, или тем более с Страуструпом не буду - поверю, что дать возможность компилятору сварганить крутой default constr лучше.


Но моё мнение, полный бред. Особенно, при good practice мы упираемся рогами в Foo{"Hello", 12}
0
118 / 93 / 35
Регистрация: 18.12.2012
Сообщений: 654
14.03.2017, 01:56  [ТС] 19
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
http://rextester.com/KAGWC31525 _t(_q)
_q забыли просто вписать в класс 'A'. С ней нет никаких ошибок, и _q нормально видится в списке инициализации. Но только для чтения.
http://rextester.com/KXUTNA47635
0
Игогошка!
1800 / 707 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
14.03.2017, 01:58 20
Цитата Сообщение от rikimaru2013 Посмотреть сообщение
Особенно, при good practice мы упираемся рогами в Foo{"Hello", 12}
Это ты про что?
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
14.03.2017, 01:58

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

Инициализация вещественных статических константных полей класса С++
static const double x=2.15; //c double и float не инициализирует. Почему??

Инициализация полей структуры в конструкторе по-умолчанию класса
Всем привет! #include &lt;string&gt; #include &lt;vector&gt; #pragma once class StudentsList {...

Ошибка при наследовании класса
Всем доброго времени суток, пытаюсь отнаследовать класс, вот такой код я смастерил: #ifndef...

Ошибки при наследовании класса
Доброго времени суток!Возникли ошибки при компиляции кода Задание было такое:Создать абстрактный...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.