Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.60/15: Рейтинг темы: голосов - 15, средняя оценка - 4.60
9 / 9 / 0
Регистрация: 22.01.2012
Сообщений: 59

Почему не наследуются конструкторы?

17.02.2015, 15:37. Показов 3351. Ответов 30
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте. Гугл говорит, что конструкторы в C++ унаследовать никак нельзя. Почему так? В чём проблема с их наследованием?
Хотел написать вектор, у которого operator [] переопределён на at()
C++
1
2
3
4
5
6
template <class T>
struct svector : private vector <T> {
    T & operator [] (int n) {
        return this -> at (n);
    }
};
Но так теряются конструкторы. Спасает вот такой костыль:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <class T>
struct svector : private vector <T> {
    T & operator [] (int n) {
        return this -> at (n);
    }
 
    svector () : vector <T> () {}
 
    template <class T1>
    svector (const T1 &a) : vector <T> (a) {}
 
    template <class T1, class T2>
    svector (const T1 &a, const T2 &b) : vector <T> (a, b) {}
};
Но это уже костыль. Так что не тру)
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
17.02.2015, 15:37
Ответы с готовыми решениями:

Наследуются ли конструкторы?
Привет. Хочу спросить, если базовый класс имеет открытый конструктор по умолчанию, то при открытом наследовании будет ли создан...

Наследуются ли конструкторы и деструкторы?
При наследовании происходит ли наследование конструкторов и деструкторов базового класса?

Почему не наследуются переменные ?
Здравствуйте, столкнулся с такой проблемой в классах наследниках не наследуются переменные родителя. В чем ошибка? Заранее спасибо. ...

30
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,035
Записей в блоге: 1
17.02.2015, 15:45
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
Спасает вот такой костыль
C++
1
2
3
4
5
6
template <typename T>
struct svector : public std::vector<T>
{
   template<typename ... Args>
   svector(Args... args) : std::vector<T>(args...) {}
};
2
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
17.02.2015, 15:45
C++
1
2
3
4
5
6
7
8
9
10
11
12
template <class T>
struct svector : private vector <T> {
 
    typedef vector <T>
        parent;
 
    using parent::parent;
 
    T & operator [] (int n) {
        return this -> at (n);
    }
};
1
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,035
Записей в блоге: 1
17.02.2015, 15:48
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
Почему так?
А как Вы себе это представляете?
Ну вот есть в наследнике конструктор с определенным параметром и в базовом есть - конфликт.
Не хочу я наследовать конструкторы(чаще так и бывает), тогда нужно все конструкторы базового класса прятать куда-то, тоже не тру.
А при множественном наследовании как быть? А с конструктором копий?
0
9 / 9 / 0
Регистрация: 22.01.2012
Сообщений: 59
17.02.2015, 15:51  [ТС]
Croessmah, Как и с обычными методами. Если два одинаковых конструктора, в приорете тот, который у наследника.
При множественном наследовании проблем не вижу.
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
17.02.2015, 15:58
Цитата Сообщение от Croessmah Посмотреть сообщение
А как Вы себе это представляете?
Вот так я это себе представляю:

http://rextester.com/NUNQFZ68981

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
//Title of this code
//Compiler Version 18.00.21005.1 for x86
 
#include <iostream>
#include <vector>
#include <stdexcept>
 
template<class T>
struct svector : private ::std::vector<T>
{
    typedef ::std::vector<T>
        parent;
 
    using parent::parent;
 
    T& operator[](const size_t n)
    {
        return this->at(n);
    }
    const T& operator[](const size_t n)const
    {
        return this->at(n);
    }
   
};
 
int main()
{
    std::cout << "Hello, world!\n";
    
    const int ar[]={1,2,3};
    svector<int> example(ar, ar+3);
    
    try{
        example[10];
    }
    catch(const std::out_of_range& e)
    {
        std::cout<<"OUT OF RANGE: " << e.what()<<'\n';
    }
    
    catch(const std::exception& e)
    {
        std::cout<<e.what()<<'\n';
    }
    
    
}
1
9 / 9 / 0
Регистрация: 22.01.2012
Сообщений: 59
17.02.2015, 15:59  [ТС]
Croessmah, ваш код у меня не компилируется
(mingw-32, -std=C++11)
C++
1
error: 'using svector<T>::parent::parent' conflicts with a previous declaration
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
17.02.2015, 16:01
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
private vector <T>
Если наследник собственных полей не добавляет, то для того,
что б слегка поменять логику отдельного метода наследоваться приватно не обязательно.


И что б вы знали: для передачи количества или индексов массивов используют беззнаковый size_t.
1
9 / 9 / 0
Регистрация: 22.01.2012
Сообщений: 59
17.02.2015, 16:38  [ТС]
Цитата Сообщение от Croessmah Посмотреть сообщение
А как Вы себе это представляете?
Странно видеть от вас такой вопрос, когда вы сами предложили полноценное (как мне кажется) решение

Добавлено через 1 минуту
hoggy, И всё-таки на mingw не компилируется. Как у меня, так и в онлайн-компиляторе (http://www.onlinecompiler.net/)

Добавлено через 15 минут
hoggy, а ещё я вовсе не понимаю, что это за строчка такая
Цитата Сообщение от hoggy Посмотреть сообщение
C++
1
using parent::parent;
0
Эксперт С++
4986 / 3093 / 456
Регистрация: 10.11.2010
Сообщений: 11,170
Записей в блоге: 10
17.02.2015, 16:53
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
а ещё я вовсе не понимаю, что это за строчка такая
В C++11 это называется наследование конструкторов (inheriting constructors).
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
17.02.2015, 17:34
Лучший ответ Сообщение было отмечено Андрей Xomach как решение

Решение

Цитата Сообщение от Андрей Xomach Посмотреть сообщение
И всё-таки на mingw не компилируется. Как у меня, так и в онлайн-компиляторе (http://www.onlinecompiler.net/)
1. Могу порекомендовать вам,
а так же онлайн сервису на который вы ссылаетесь, обновить компилятор.

свежий компилятор gcc, который идет в коробке mingw поддерживает с++11
http://ideone.com/DMBDmm

------------------------------------------------------------------------

2. Либо использовать более "старую" технику основанную на variardic template.

Например, так это можно реализовать для компилятора cl (вижуал студия),
который плохо поддерживает с++11

http://rextester.com/MNLCQ86757

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
//Title of this code
//Compiler Version 18.00.21005.1 for x86
 
#include <iostream>
#include <utility>
#include <vector>
#include <stdexcept>
 
template<class T>
struct svector : private ::std::vector<T>
{
    typedef ::std::vector<T>
        parent;
 
    template<typename ... Args>
    svector(Args&&... args) 
        : parent( std::forward<Args>(args)... ) 
    {}
 
    T& operator[](const size_t n)
    {
        return this->at(n);
    }
    const T& operator[](const size_t n)const
    {
        return this->at(n);
    }
   
};
 
int main()
{
    std::cout << "Hello, world!\n";
    
    const int ar[]={1,2,3};
    svector<int> example(ar, ar+3);
    
    try{
        example[10];
    }
    catch(const std::out_of_range& e)
    {
        std::cout<<"OUT OF RANGE: " << e.what()<<'\n';
    }
    
    catch(const std::exception& e)
    {
        std::cout<<e.what()<<'\n';
    }
}
Особенное внимание обратите на конструкцию конструктора:

C++
1
2
3
4
    template<typename ... Args>
    svector(Args&&... args)                //<--- универсальная ссылка
        : parent( std::forward<Args>(args)... )  //<--- аргументы передаются, как форварды
    {}
Универсальные ссылки нужны, для оптимизации передачи аргументов:

-- избежать копирования аргументов
-- аргументы могут быть как lvalue, так и rvalue

std::forward нужен, для оптимизации запуска конструктора.
Благодаря ему становится возможным запуск move-конструктора

------------------------------------------------------------------------

3. Если ваш компилятор слишком стар и вообще не поддерживает с++11,
то можно реализовать это так, как это делалось в рамках с++03:

C++
1
2
3
4
5
6
7
8
9
    template<class A1>
    svector(A1& a1) 
        : parent(a1) 
    {}
    
    template<class A1, class A2>
    svector(A1& a1, A2& a2) 
        : parent(a1,a2) 
    {}
И пошла копипаста под все возможное количество параметров.

В рамках с++03 стратегия оптимальной передачи аргументов - предмет особых плясков с бубнами.

В данном сообщении я этот нюанс рассматривать не буду,
поскольку полагаю с++03 в 2015 году не очень актуален.

Добавлено через 15 минут
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
а ещё я вовсе не понимаю, что это за строчка такая
using parent::parent;

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

В контексте классов, это позволяет контролировать модификаторы доступа к унаследованному функционалу.

Пример:

http://rextester.com/TEKK89627


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
//Title of this code
//Compiler Version 18.00.21005.1 for x86
 
#include <iostream>
using namespace std;
 
struct base
{
    void foo()const { cout<<"base::foo\n"; }
    void bar()const { cout<<"base::bar\n"; }
};
 
 
struct der1: private base 
{
    // теперь снаружи доступен метод базового класса
    using base::foo;  
    
protected:
 
    // теперь метод доступен всем потомкам
    using base::bar;  
    
};
 
struct der2: base 
{
    //мы унаследовались публично, но хотим сделать некоторые методы недоступными снаружи
    //просто объявим их в закрытой секции:    
    
    void work()const { this->bar(); };
    
private:
    
    // теперь метод доступен только изнутри класса
    using base::bar;  
    
    
    
    
};
 
 
int main()
{
    std::cout << "Hello, world!\n";
    
    der1().foo(); //<--- работает, потому что объявлен в публичной области видимости
    
    der2().work(); 
    
}
Все что требуется от программиста:

Что бы открыть доступ: просто перечислить методы базового класса в публичной зоне.
Что бы закрыть доступ: просто перечислить методы базового класса в частной зоне.
Что бы защитить доступ: просто перечислить методы базового класса в защищенной зоне.
1
9 / 9 / 0
Регистрация: 22.01.2012
Сообщений: 59
17.02.2015, 17:59  [ТС]
hoggy, огромное вам спасибо, вспомнил и узнал много нового.
Вот только в реализации для C++03 не лучше бы использовать const A1 & чтобы аргумент мог быть rvalue?
0
Эксперт С++
 Аватар для hoggy
8973 / 4319 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
17.02.2015, 18:16
Цитата Сообщение от Андрей Xomach Посмотреть сообщение
для C++03 не лучше бы использовать const A1 & чтобы аргумент мог быть rvalue?
Это не будет работать для функций, которые ожидают ссылки на изменяемые объекты.

Проблема оптимальной стратегии передачи аргументов в с++03 это "легаси-косяк-в-дизайне-языка".

Рассмотрим пример:

http://rextester.com/KMOTF77045

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
//Title of this code
//Compiler Version 18.00.21005.1 for x86
 
#include <iostream>
 
// хочет ссылку, по которой можно изменить оригинальный объект
struct foo
{
    foo(int& val){}
};
 
// хочет ссылку, по которой изменить оригинальный объект нельзя
struct bar
{
    bar(const int& val){}
};
 
 
template<class T>
struct der: T
{
    template<class U>
    der(U& v)
        : T(v) 
    {}
    
};
 
 
int main()
{
    std::cout << "Hello, world!\n";
    
    int v1 = 10;
    
    const int v2 = 10;
    
    der<foo> f(v1);
    der<bar> b(v2);
}
Мы наблюдаем успешную компиляцию.


Для константного аргумента шаблон раскроется в const T&
А для не константного аргумента раскроется в T&

Поэтому, это будет работать в обоих случаях.

Но есть проблема:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// хочет ссылку, по которой изменить оригинальный объект нельзя
struct bar
{
    bar(const int& val){}
};
 
 
template<class T>
struct der: T
{
    template<class U>
    der(U& v)
        : T(v) 
    {}
    
};
 
int main()
{
    der<bar> b(100); //<--- ошибка
 // error C2664: 'der<bar>::der(const der<bar> &)' : cannot convert argument 1 from 'int' to 'int &'
 
}
"легаси-косяк-в-дизайне-языка" заключается в том,
что компилятор считает, что литерные числовые константы имеют тип данных, который не является const

Циферку 100 он попытался передать через T&, а не через const T&

На самом деле, циферка 100 - это rvalue, которое само по себе не является const.

Однако, поскольку в рамках с++03 пользователи не могут работать с rvalue явным образом,
то попытка сделать универсально превращается в аццкие пляски с бубнами.
Любое решение оказывается не тривиальным.

В простейшем случае (когда всего 1 аргумент) достаточно посадить рядышком ещё один конструктор,
у которого аргумент явный const T&

Но если аргументов может быть много, и некоторые из них должны раскрываться, как T&, а другие - как const T&,
то начинается увлекательное путешествие в мир граблей языка с++
1
28 / 28 / 5
Регистрация: 23.04.2014
Сообщений: 130
17.02.2015, 23:31
Конструктор предка и так содержится в в наследнике, при вызове конструктора наследника по цепочке вызываются все конструкторы предков. Поэтому нет смысла его копировать.
Например, есть класс
C++
1
2
3
4
5
6
7
struct parent
{
    parent(type_a a, type_b b) : a(a), b(b)
    {
        //some code
    }
};
и есть класс
C++
1
2
3
4
5
6
7
8
struct child : parent
{
    child(type_a a, type_b b, type_c c) : parent(a, b), c(c)
    {
        //some code
    }
};
}
То же самое и с деструктором, при его вызове по цепочке вызывают все деструкторы предков. Поэтому деструктор всегда должен иметь определение, даже если он чисто виртуальный

Добавлено через 6 минут
Не успел откорректировать сообщение)

В этом коде видно, что можно вызывать явно конструктор предка. Т.е. сначала вызывается конструктор наследника, затем конструктор предка, и так далее по цепочке.

С деструктором та же ситуация, при его вызове по той же самой цепочке, но в обратном порядке вызываются все деструкторы предков. Поэтому деструктор всегда должен иметь определение, даже если он чисто виртуальный
0
654 / 575 / 164
Регистрация: 13.12.2012
Сообщений: 2,124
18.02.2015, 10:53
Цитата Сообщение от GREGOR_812 Посмотреть сообщение
Т.е. сначала вызывается конструктор наследника, затем конструктор предка, и так далее по цепочке.
ну сначала так то, вызовется конструктор самого первого родителя
0
Неэпический
 Аватар для Croessmah
18149 / 10731 / 2067
Регистрация: 27.09.2012
Сообщений: 27,035
Записей в блоге: 1
18.02.2015, 12:17
Цитата Сообщение от aLarman Посмотреть сообщение
ну сначала так то, вызовется конструктор самого первого родителя
Сначала вызываются конструкторы дочерних. Это уже они вызывают конструкторы предков.
Цитата Сообщение от hoggy Посмотреть сообщение
Вот так я это себе представляю
ну тут же Вы указываете явно что использовать с помощью using-declaration. По умолчанию они не наследуются. А я именно к положению "по-умолчанию", но всё равно спасибо за замечание. Я уже забыл об этом.
0
18.02.2015, 13:03

Не по теме:

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

0
28 / 28 / 5
Регистрация: 23.04.2014
Сообщений: 130
18.02.2015, 14:20
Цитата Сообщение от aLarman Посмотреть сообщение
завершается создание объекта конструктором наследника(точнее инициализацией его полей)
Инициализация полей может происходить до исполнения конструктора
0
18.02.2015, 14:33

Не по теме:

Цитата Сообщение от GREGOR_812 Посмотреть сообщение
Инициализация полей может происходить до исполнения конструктора
Интересно было бы посмотреть на пример :)

0
:)
Эксперт С++
4773 / 3267 / 497
Регистрация: 19.02.2013
Сообщений: 9,046
18.02.2015, 15:19
Цитата Сообщение от DrOffset Посмотреть сообщение
Интересно было бы посмотреть на пример
Вероятно речь об этом:
C++
1
2
3
4
5
struct C
{
   C() { /* здесь i уже проинициализировано */ }
   int i = 42;
};
Хотя аналогичное достигается и в до С++11 версиях через инициализатор конструктора. Который тоже выполняется до тела конструктора.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
18.02.2015, 15:19
Помогаю со студенческими работами здесь

Конструкторы. Почему-то по умолчанию и копирующий не срабатывают, помогите найти ошибку.
Написал небольшой код, для понимания, как работают различные варианты конструкторов (т.е. по умолчанию, с параметром и копирующий), но...

Почему наследуются свойства применяемые в таблице
Здравствуйте! Есть две страницы на каждой находится таблица. Я прописала свойства для первой таблицы, где мне нужно, чтобы border...

Почему в описании языка "интерфейсы наследуются"?
Источник, цитата: Интерфейсы реализуются, а не наследуются...

Webpack собирает проект, а приложение говорит что мои конструкторы не конструкторы
Помогите пожалуйста, в едином файле (не билде) всё работает хорошо, как только начинаю отделять классы и создавать билд, сразу ошибка в...

Не наследуются стили
Оформляю меню нестандартными шрифтами. Возникла следующая проблема: категории и подкатегории никак не хотят оформляться разными шрифтами. ...


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

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Модель заражения группы наркоманов
alhaos 17.04.2026
Условия задачи сформулированы тут Суть: - Группа наркоманов из 10 человек. - Только один инфицирован ВИЧ. - Колются одной иглой. - Колются раз в день. - Колются последовательно через. . .
Мысли в слух. Про "навсегда".
kumehtar 16.04.2026
Подумалось тут, что наверное очень глупо использовать во всяких своих установках понятие "навсегда". Это очень сильное понятие, и я только начинаю понимать край его смысла, не смотря на то что давно. . .
My Business CRM
MaGz GoLd 16.04.2026
Всем привет, недавно возникла потребность создать CRM, для личных нужд. Собственно программа предоставляет из себя базу данных клиентов, в которой можно фиксировать звонки, стадии сделки, а также. . .
Знаешь почему 90% людей редко бывают счастливыми?
kumehtar 14.04.2026
Потому что они ждут. Ждут выходных, ждут отпуска, ждут удачного момента. . . а удачный момент так и не приходит.
Фиксация колонок в отчете СКД
Maks 14.04.2026
Фиксация колонок в СКД отчета типа Таблица. Задача: зафиксировать три левых колонки в отчете. Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка) / / . . .
Настройки VS Code
Loafer 13.04.2026
{ "cmake. configureOnOpen": false, "diffEditor. ignoreTrimWhitespace": true, "editor. guides. bracketPairs": "active", "extensions. ignoreRecommendations": true, . . .
Оптимизация кода на разграничение прав доступа к элементам формы
Maks 13.04.2026
Алгоритм из решения ниже реализован на нетиповом документе, разработанного в конфигурации КА2. Задачи, как таковой, поставлено не было, проделанное ниже исключительно моя инициатива. Было так:. . .
Контроль заполнения и очистка дат в зависимости от значения перечислений
Maks 12.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеПерсонала", разработанного в конфигурации КА2. Задача: реализовать контроль корректности заполнения дат назначения. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru