2015-02-06 5 views
2

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

Мне непонятно, потребует ли эта корректировка указателя не полиморфное наследование.

Очень простой пример был бы:

struct Base1 { 
    void b1() {} 
}; 

struct Base2 { 
    void b2() {} 
}; 

struct Derived : public Base1, Base2 { 
    void derived() {} 
}; 

ли следующий вызов функции требуют такой корректировки указателя?

Derived d; 
d.b2(); 

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

Я понимаю, что это деталь реализации, а не часть стандарта C++, но это вопрос о том, как ведут себя настоящие компиляторы. Я не знаю, является ли это примером, например, vtables, где все компиляторы следуют одной и той же общей стратегии или если я задал вопрос, зависящий от компилятора. Если он очень зависим от компилятора, то это само по себе будет достаточным ответом или, если хотите, вы можете сосредоточиться на gcc или clang.

+0

Я не вижу никаких указателей. Существует ясный, прямой, конкретный путь к методу 'b1', поэтому vtable может не понадобиться. Помните, что * vtables * не требуются спецификацией языка C++. Компилятор может вызвать метод напрямую. –

ответ

4

Внешний вид объекта не указан языком. С ++ проект стандарта N3337 C:

10 производных классах

5 Порядок, в котором подобъекты базового класса выделяются в наиболее производный объект (1.8) не определен. [Примечание: производный класс и субобъекты его базового класса могут быть представлены направленным ациклическим графом (DAG), где стрелка означает «непосредственно полученную из». DAG подобъектов часто называют «подобъектной решеткой».

enter image description here

6 стрелки не должны иметь физическое представление в памяти. -end примечание]

Coming на ваш вопрос:

ли следующий вызов функции требуют такой корректировки указателя?

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

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

Вот несколько примеров кода и выход из работы кода:

#include <iostream> 

struct Base1 { 
    void b1() 
    { 
     std::cout << (void*)this << std::endl; 
    } 
    int x; 
}; 

struct Base2 { 
    void b2() 
    { 
     std::cout << (void*)this << std::endl; 
    } 
    int y; 
}; 

struct Derived : public Base1, public Base2 { 
    void derived() {} 
}; 

int main() 
{ 
    Derived d; 
    d.b1(); 
    d.b2(); 
    return 0; 
} 

Выход:

 
0x28ac28 
0x28ac2c 
+0

Хорошо, я должен был использовать член второго базового класса. Я даже не думал об этом, хорошая идея. – Praxeolitic

1

Это не только специфический для компилятора, но и определенный уровень оптимизации. Как правило, все указатели this настроены, только иногда оно равно 0, как и ваш пример во многих компиляторах (но определенно не во всех - IIRC, MSVC является заметным исключением). Если функция встроена и не имеет доступа к this, то настройка может быть полностью оптимизирована.

0

С помощью метода R Sahu для тестирования этого, он выглядит как ответ на НКУ, лязг, и icc да, эта корректировка указателя происходит, если базовый класс не является основным базовым классом или пустым базовым классом.

Теста-код:

#include <iostream> 

namespace { 
struct Base1 
{ 
    void b1() 
    { 
     std::cout << "b1() " << (void*)this << std::endl; 
    } 

    int x; 
}; 

struct Base2 
{ 
    void b2() 
    { 
     std::cout << "b2() " << (void*)this << std::endl; 
    } 

    int x; 
}; 

struct EmptyBase 
{ 
    void eb() 
    { 
     std::cout << "eb(): " << (void*)this << std::endl; 
    } 
}; 

struct Derived : private Base1, Base2, EmptyBase 
{ 
    void derived() 
    { 
     b1(); 
     b2(); 
     eb(); 
     std::cout << "derived(): " << (void*)this << std::endl; 
    } 
}; 
} 

int main() 
{ 
    Derived d; 
    d.derived(); 
} 

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

Пример G ++ 4.9.2 Выход:

b1() 0x7fff5c5337d0 
b2() 0x7fff5c5337d4 
eb(): 0x7fff5c5337d0 
derived(): 0x7fff5c5337d0 

Пример лязг 3.5.0 Выход

b1() 0x7fff43fc07e0 
b2() 0x7fff43fc07e4 
eb(): 0x7fff43fc07e0 
derived(): 0x7fff43fc07e0 

Пример МТП 15.0.0.077 выход:

b1() 0x7fff513e76d8 
b2() 0x7fff513e76dc 
eb(): 0x7fff513e76d8 
derived(): 0x7fff513e76d8 

Все три Составители регулировки этот указатель для b2(). Если они не справятся с этой настройкой указателя в этом легком случае, то они, скорее всего, никогда не справятся с этой корректировкой указателя. Основной базовый класс и пустые базовые классы - это исключения.

Насколько я знаю, интеллектуальный стандарт, соответствующий компилятору, может ускорить эту настройку указателя для b2(), но это просто оптимизация, которую они не делают.

 Смежные вопросы

  • Нет связанных вопросов^_^