Войти
ПрограммированиеФорумОбщее

C++ Как статически проверить что базовый тип - первый?

Страницы: 1 2 3 Следующая »
#0
11:09, 30 июля 2017

Мне нужно проверить:

class Derived : publuc Base1, public Base2 {};

Derived* derived;

derived == (Base1*)derived // true, так можно проверить динамически, что Base1 является первым базовым классом, как сделать тоже самое статически?
derived == (Base2*)derived // false

Мне нужно что то типа: std::is_first_base_of<TBase, TDerived>::value, есть такое?

Суть в том, что мне нужно вызывать delete для базового указателя, а для этого соответственно нужно чтобы указатель был не смещенным, то есть нельзя вызывать delete для Base2* указателя (или можно?).


#1
11:30, 30 июля 2017

Если у базовых классов деструктор виртуальный, то его вызов приведет к вызову правильных деструкторов у наследника и всех баз.
Получить указатель на начало памяти, имея указатель на одну из баз можно через dynamic_cast<void*> конструкт (тоже требует наличие vtable).
http://rextester.com/KLD63999

#2
11:37, 30 июля 2017

gamedeveloper01
Тебе известно что-нибудь о множественном наследование?
http://www.amse.ru/courses/cpp1/2010.05.14.html

#3
11:47, 30 июля 2017

Funtik
> Тебе известно что-нибудь о множественном наследование?
Ты тупой?

#4
11:51, 30 июля 2017

return [](){};
> Если у базовых классов деструктор виртуальный, то его вызов приведет к вызову
> правильных деструкторов у наследника и всех баз.
> Получить указатель на начало памяти, имея указатель на одну из баз можно через
> dynamic_cast<void*> конструкт (тоже требует наличие vtable).
То чувство когда просишь простое статическое решение, а тебе кидают dynamic_cast, при том что и без него можно обойтись в динамике, о чем написано в начале.

#5
11:52, 30 июля 2017

gamedeveloper01
>derived == (Base1*)derived // true, так можно проверить динамически, что Base1 является первым базовым классом, как сделать тоже самое статически?
>derived == (Base2*)derived // false
Глядя на это бред, тупой из нас ты!

#6
11:59, 30 июля 2017

Funtik
> Глядя на это бред, тупой из нас ты!
Откуда такие дауны берутся как ты? иди учи C++ понос школьный и не лезь в сложные темы.

#7
12:04, 30 июля 2017

gamedeveloper01
сынок, твоя тема не сложная, а тупая!!!

#8
12:19, 30 июля 2017

http://ideone.com/oJJZDY

Всё-таки автору надо подучить плюсцы. Можно вызывать любой деструктор и не заниматься херней.

#9
13:36, 30 июля 2017

gamedeveloper01
> Мне нужно что то типа: std::is_first_base_of<TBase, TDerived>::value, есть
> такое?

нет.

1.
этого нет в стандартной библиотеке.

2.
реализовать мета-функцию средствами самого языка невозможно.

3.
сама по себе механика "множественного наследования"
является Implementation-defined behavior

это значит,
что вы вообще не должны закладываться на порядок объявлений базовых классов.

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

пример:
во многих реализациях адрес подобъекта первого базового класса
совпадает с  this агрегата, а адрес второго - с некоторым смещением:

http://rextester.com/SMKF44415

#include <iostream>

struct Base1
{
    void view()const noexcept 
        { std::cout << "base1: " << this << std::endl; }
    
    int v1;
};
struct Base2
{
    void view()const noexcept 
        { std::cout << "base2: " << this << std::endl; }
    
    int v2;
};

struct Derived : Base1, Base2 
{
    int v;
    void view()const noexcept 
        { std::cout << "Derived: " << this << std::endl; }        
    
    void view_all()const noexcept 
        { 
            Base2::view();
            Base1::view();
            Derived::view();
        }        
};


int main()
{
    Derived der;
    der.view_all();
    /*
    output:
    base2: 0x7ffd3e1ea634
    base1: 0x7ffd3e1ea630
    Derived: 0x7ffd3e1ea630
    */
}

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

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

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

gamedeveloper01
> Суть в том, что мне нужно вызывать delete для базового указателя, а для этого
> соответственно нужно чтобы указатель был не смещенным, то есть нельзя вызывать
> delete для Base2* указателя (или можно?).

нет никаких "соответственно".

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

итого: вы решаете проблему XY.

прежде чем постить тему,
можно было бы хотя б просто написать код,
да посмотреть: работает, или нет.

ArchiDevil
> Всё-таки автору надо подучить плюсцы. Можно вызывать любой деструктор и не
> заниматься херней.

+1

#10
14:02, 30 июля 2017

ArchiDevil
> Всё-таки автору надо подучить плюсцы. Можно вызывать любой деструктор и не
> заниматься херней.
Причем тут деструктор тупой даун. Мне нужен указатель для освобождения памяти. Читай первый пост тупой баран, я же сказал функция DELETE/FREE, а не ДЕСТРУКТОР. Тупая дура. Почуй разницу. Мне нужно по указателю удалить участок ПАМЯТИ, ДУРА ТУПАЯ!!!

Короче, пока с вами баранами тут тёрся, нашел еще и баг студии.

derived == (Base2*)derived // выдаёт всегда true.
(void*)derived == (Base2*)derived // правильно сравнивает и возвращает false.

А всё из-за чего? да потому что сейчас в микрософтах работают такие же бараны как на этом форуме сидят.

#11
14:08, 30 июля 2017

Kartonagnick
Это конечно весело, что стандартом не предусмотрено, но как то пофиг что там эти бараны предусмотрели, а что нет. По факту это уже давно стандарт, что первый наследник идет вначале памяти.

Я правильно понял, ты утверждаешь, что если у Base2 есть virtual destructor, то в функцию delete(void* ptr) попадает именно начальный указатель? а не Base2*? если так, то это чудесно, есть пруфы?

UPD: нифига подобного:

#include <iostream>

struct Base1
{
    void view()const noexcept 
        { std::cout << "base1: " << this << std::endl; }
    
    int v1;
};
struct Base2
{
    virtual ~Base2()
    {
        
    }
    
    void view()const noexcept 
        { std::cout << "base2: " << this << std::endl; }
    
    int v2;
};

struct Derived : Base1, Base2 
{
    int v;
    void view()const noexcept 
        { std::cout << "Derived: " << this << std::endl; }        
    
    void view_all()const noexcept 
        { 
            Base2::view();
            Base1::view();
            Derived::view();
        }        
};


void operator delete(void * p) throw()
{
 std::cout << "del: " << p << std::endl; 
}

int main()
{
    Derived* der = new Derived();
    der->view_all();
    
    Base2* b2 = der;
    delete b2;
}

UPD2: да, именно так. В первый раз не правильно прочитал результат из-за поменявшегося порядка наследования компилятором в следствие появившегося виртуального деструктора.

#12
14:57, 30 июля 2017

gamedeveloper01
> Короче, пока с вами баранами тут тёрся, нашел еще и баг студии.
>
> derived == (Base2*)derived // выдаёт всегда true.
> (void*)derived == (Base2*)derived // правильно сравнивает и возвращает false.
>
> А всё из-за чего?

из-за того, что вы не знаете язык с++.
здесь нет никакого бага.

рассмотрим по шагам:

    Derived* derived = &der;

    Base2* p = (Base2*)derived; //(1) p != &der
    
    if(derived == p)
        std::cout << "(2) derived == (Base2*)derived)\n";
        
    if((void*)derived != p)
        std::cout << "(3) derived != (Base2*)derived)\n";

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

4.10.3

4.10 Pointer conversions [conv.ptr]
3 A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer
to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2)
base class of D, a program that necessitates this conversion is ill-formed.

таким образом, полученный указатель оказывается не равен p != derived,
потому что получается со смещением для подобъекта Base2.
согласно Implementation-defined behavior компилятора
(на самом деле поведение всех топовых компиляторов cl/clang/gcc здесь одинаковое)

далее, смотрим на второй случай:

if(derived == p)
сравнение двух указателей разного типа,
но связанные родственными отношениями одной иерархии классов.

это попадает под правило стандарта 5.10.2

5.10 Equality operators [expr.eq]
2 If at least one of the operands is a pointer, pointer conversions (4.10) and qualification conversions (4.4) are
performed on both operands to bring them to their composite pointer type (Clause 5). Comparing pointers
is defined as follows: Two pointers compare equal if they are both null, both point to the same function, or
both represent the same address (3.9.2), otherwise they compare unequal.

другими словами, в операции сравнении:

if(derived == p)
указатель на наследника приводится к указателю на базу.
это равносильно:
if(Base2*)derived == p)

очевидно, что они получаются равными.

в третьем случае сравнение идет с указателем на void*

здесь уже не известен фактический тип объекта.
и компилятору ничего не остается, кроме как
сравнивать адреса по факту, "как есть".

случай приведения указателя к void*
описывается в 4.10 Pointer conversions [conv.ptr]
стандарта языка.


gamedeveloper01
> да потому что сейчас в микрософтах работают такие же бараны как на этом форуме
> сидят.

эй, скрипач!
ты...
горяч...
как...
всегда...
строптив...

+ Показать

gamedeveloper01
> Это конечно весело, что стандартом не предусмотрено, но как то пофиг что там
> эти бараны предусмотрели, а что нет.

напротив.
как раз таки подобные нюансы представляют особенный интерес.

некоторые вещи не включены в стандарт (либо определены, как зависящие от реализации)
по вполне объективными причинам.

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

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

gamedeveloper01
> По факту это уже давно стандарт, что первый наследник идет вначале памяти.
если только в вашей маленькой вселенной.
с++ не ограничен одними лишь студийными компиляторами.

gamedeveloper01
> Я правильно понял, ты утверждаешь, что если у Base2 есть virtual destructor, то
> в функцию delete(void* ptr) попадает именно начальный указатель? а не Base2*?
> если так, то это чудесно, есть пруфы?

в сообщении #8 господин ArchiDevil специально для вас оставил ссылку на онлайн-компилятор.
вы бы это увидели, не будь вы столь спесивы.

#13
15:01, 30 июля 2017

gamedeveloper01
> По факту это уже давно стандарт, что первый наследник идет вначале памяти
А какой конкретно стандарт?
Наверное, Вы имели ввиду порядок указателей в вирт. таблице указателей на вирт. функции созданного фабрикой классов?!

#14
15:09, 30 июля 2017

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

Ну что я могу сказать, неплохо так тут знают C++, диванные кукаретики. Веселых вам утечек памяти.

Ответ на тему был получен, он итак был известен на 99%, просто мало ли, вдруг завезли.

Страницы: 1 2 3 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.