просмотреть функции по умолчанию, сгенерированные компилятором? - PullRequest
8 голосов
/ 25 января 2010

Есть ли способ просмотреть функции по умолчанию (например, конструктор копирования по умолчанию, оператор назначения по умолчанию), сгенерированные компилятором, таким как VC ++ 2008, для класса, который их не определяет?

Ответы [ 5 ]

11 голосов
/ 25 января 2010

С помощью компилятора clang вы можете увидеть их, передав аргумент -ast-dump. Clang все еще находится в стадии разработки, но вы уже можете использовать его для следующих целей:

[js@HOST2 cpp]$ cat main1.cpp
struct A { };
[js@HOST2 cpp]$ clang++ -cc1 -ast-dump main1.cpp
typedef char *__builtin_va_list;
struct A {
public:
    struct A;
    inline A();
    inline A(struct A const &);
    inline struct A &operator=(struct A const &);
    inline void ~A();
};
[js@HOST2 cpp]$

Надеюсь, ты об этом и просил. Давайте изменим код и посмотрим снова.

[js@HOST2 cpp]$ cat main1.cpp
struct M { M(M&); };
struct A { M m; };
[js@HOST2 cpp]$ clang++ -cc1 -ast-dump main1.cpp
typedef char *__builtin_va_list;
struct M {
public:
    struct M;
    M(struct M &);
    inline struct M &operator=(struct M const &);
    inline void ~M();
};
struct A {
public:
    struct A;
    struct M m;
    inline A();
    inline A(struct A &);
    inline struct A &operator=(struct A const &);
    inline void ~A();
};
[js@HOST2 cpp]$

Обратите внимание, что неявно объявленный конструктор копирования A теперь имеет неконстантный ссылочный параметр, потому что у одного из его членов тоже есть (член m), а у M не объявлен конструктор по умолчанию.

Для получения сгенерированного кода вы можете разрешить ему использовать промежуточный язык виртуальной машины. Давайте посмотрим на сгенерированный код для этого:

struct A { virtual void f(); int a; };
A f() { A a; a = A(); return a; } // using def-ctor, assignment and copy-ctor

[js@HOST2 cpp]$ clang++ -cc1 -O1 -emit-llvm -o - main1.cpp | c++filt
[ snippet ]
define linkonce_odr void @A::A()(%struct.A* nocapture %this) nounwind align 2 {
entry:
  %0 = getelementptr inbounds %struct.A* %this, i32 0, i32 0 ; <i8***> [#uses=1]
  store i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2), i8*** %0
  ret void
}

define linkonce_odr %struct.A* @A::operator=(A const&)(%struct.A* %this, 
  %struct.A* nocapture) nounwind align 2 {
entry:
  %tmp = getelementptr inbounds %struct.A* %this, i32 0, i32 1 ; <i32*> [#uses=1]
  %tmp2 = getelementptr inbounds %struct.A* %0, i32 0, i32 1 ; <i32*> [#uses=1]
  %tmp3 = load i32* %tmp2                         ; <i32> [#uses=1]
  store i32 %tmp3, i32* %tmp
  ret %struct.A* %this
}

define linkonce_odr void @A::A(A const&)(%struct.A* nocapture %this, %struct.A* nocapture) 
  nounwind align 2 {
entry:
  %tmp = getelementptr inbounds %struct.A* %this, i32 0, i32 1 ; <i32*> [#uses=1]
  %tmp2 = getelementptr inbounds %struct.A* %0, i32 0, i32 1 ; <i32*> [#uses=1]
  %tmp3 = load i32* %tmp2                         ; <i32> [#uses=1]
  store i32 %tmp3, i32* %tmp
  %1 = getelementptr inbounds %struct.A* %this, i32 0, i32 0 ; <i8***> [#uses=1]
  store i8** getelementptr inbounds ([3 x i8*]* @vtable for A, i32 0, i32 2), i8*** %1
  ret void
}

Теперь я не понимаю этот промежуточный язык (который определен в llvm.org ). Но вы можете перевести весь этот код в C, используя компилятор llvm:

[js@HOST2 cpp]$ clang++ -cc1 -O1 -emit-llvm -o - main1.cpp | llc -march=c -o - | c++filt
[snippet]
void A::A()(struct l_struct.A *llvm_cbe_this) {
  *((&llvm_cbe_this->field0)) = ((&_ZTV1A.array[((signed int )2u)]));
  return;
}


struct l_struct.A *A::operator=(A const&)(struct l_struct.A *llvm_cbe_this, struct l_struct.A
  *llvm_cbe_tmp__1) {
  unsigned int llvm_cbe_tmp3;

  llvm_cbe_tmp3 = *((&llvm_cbe_tmp__1->field1));
  *((&llvm_cbe_this->field1)) = llvm_cbe_tmp3;
  return llvm_cbe_this;
}


void A::A(A const&)(struct l_struct.A *llvm_cbe_this, struct l_struct.A *llvm_cbe_tmp__2) {
  unsigned int llvm_cbe_tmp3;

  llvm_cbe_tmp3 = *((&llvm_cbe_tmp__2->field1));
  *((&llvm_cbe_this->field1)) = llvm_cbe_tmp3;
  *((&llvm_cbe_this->field0)) = ((&_ZTV1A.array[((signed int )2u)]));
  return;
}

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

7 голосов
/ 25 января 2010

Вы можете отследить код с помощью отладчика, чтобы увидеть, что происходит. Например:

#include <string>

struct A {
    int a[100];
    char c;
    std::string s;
};

int main() {
    A a;
    A b(a);
}

Установить точку останова при построении 'b' конструктором копирования. Выходные данные ассемблера в тот момент в отладчике VC ++ 6:

12:       A b(a);
00401195   lea         eax,[ebp-1B0h]
0040119B   push        eax
0040119C   lea         ecx,[ebp-354h]
004011A2   call        @ILT+140(A::A) (00401091) 

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

Однако, если ваш вопрос «как я могу увидеть код C ++ для конструктора копирования и т. Д.», Ответ будет невозможным, потому что его нет - компилятор генерирует машинный или ассемблерный код (в зависимости ваш компилятор) для них, а не код C ++.

2 голосов
/ 25 января 2010

Методы, сгенерированные компилятором, являются абстрактными, их нет в исходном коде.
Посмотрите на пример ниже, я попытаюсь объяснить, что должны делать методы, сгенерированные компилятором four на уровне исходного кода. Отсюда вы сможете экстраполировать любой нормальный класс.

Если у вас есть такой класс:

class X: public Base
{
    int*   a;
    double b;
    Y      c;
};

Затем компилятор генерирует эквивалент следующего:

X::X() // Default constructor
    :Base() Calls the base class default constructor
    //,a    pointers are POD no default initialization
    //,b    double   are POD no default initialization
    ,c()    //Call default constructor on each non POD member
{}

X::~X() // Default destructor
{}
// Destructor for each non POD member in reverse order
~c()       calls the destructor of the class type
//~b       double are POD no destructor
//~a       pointers are POD no destructor
~Base()    // Calls the base class destructor

X::X(X const& copy)
    :Base(copy)    // calls the base class copy constructor
    // Copies each member using its copy constructor
    ,a(copy.a)     // Pointers copied  (Note just the pointer is copied, not what it points at)
    ,b(copy.b)     // Double copied.
    ,c(copy.c)     // Uses copy constructor of the class type (must be accessible)
{}

X& X::operator=(X const& copy)
{
    Base::operator=(copy);  // Calls the base class assignment operator
    // Copies each member using the members assignment operator
    a = copy.a;    // Pointers copied  (Note just the pointer is copied, not what it points at)
    b = copy.b;    // Double copied
    c = copy.c;    // Uses assignment operator of the class type (must be accessible)

    return *this;
}
0 голосов
/ 25 января 2010

Вы уверены, что вам нужно для просмотра этой функции?

По умолчанию компилятор создает их, вызывая конструктор копирования или оператор присваивания для каждой переменной-члена.

Проблема в том, что при использовании объектов, которые используют подсчет ссылок для управления данными, конструктор копирования по умолчанию создаст копию объекта, но не копию данных, на которые указывает объект. Это также относится и к указателям. Так что если у вас есть класс, как:

class aClass
{
  int one;
  int *ptwo;
};

Конструктор копирования по умолчанию копирует только данные a и указатель b. Однако он не копирует данные, на которые указывает b. Если вы используете этот класс как

aClass a, b;
a.ptwo = new int;
a.one = 1;
*(a.ptwo) = 2;

b = a;

*(b.ptwo) = 1;  
//a.ptwo now points to an integer with the value of 1

Если вы хотите, чтобы этот класс копировал значение ptwo вместо указателя, вам понадобится ваша собственная функция оператора копирования. Если вас интересует более подробная информация о том, что делают функции по умолчанию, а что нет, вы можете обратиться к книге «Эффективный C ++».
В этом материале есть целая глава, в которой объясняется, что функции по умолчанию для классов делают, не делают, что они должны делать, когда писать свои собственные. Я уверен, что вы можете получить цифровую версию в Интернете, если вы просто хотите узнать об этой функции.

0 голосов
/ 25 января 2010

Инструмент просмотра объектов (например, objdump или dumpbin ) может разобрать для вас выходные объектные файлы. Затем вы можете покопаться и посмотреть, какие инструкции выдаются для функций / методов, которые вас интересуют.

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