2016-03-31 4 views
5

Я новичок в C++, но у меня есть некоторый опыт работы на Java. Во время кодирования я наткнулся на ошибку, которая смутила меня. Вот мой код (упрощенно, но ошибки одинаковы):C++: передача этого указателя в другой класс

Ач

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

a.cpp:

#include "A.h" 
#include <iostream>  
A::A() {} 
void A::foo() { 
    b.bar(this); 
} 
void A::sayHello() { 
    std::cout << "Hello" << std::endl; 
} 

Bh:

#pragma once 
#include "A.h" 
class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 

B.cpp:

#include "B.h" 
B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

Я хочу передать указатель на объекта к бар функции в B, так что я буду иметь возможность изменять и доступ к a 's поля в bar. Как ни странно, я получаю эти ошибки, когда я называю Foo через экземпляр A из другого класса:

1>------ Build started: Project: Test, Configuration: Debug Win32 ------ 
1> main.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A' 
1> B.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C3646: 'b': unknown override specifier 
1>d:\stuff\visual studio 2015\projects\test\test\a.h(9): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
1> A.cpp 
1>d:\stuff\visual studio 2015\projects\test\test\b.h(7): error C2061: syntax error: identifier 'A' 
1>d:\stuff\visual studio 2015\projects\test\test\a.cpp(5): error C2660: 'B::bar': function does not take 1 arguments 
1> Generating Code... 
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== 

код работает отлично, если я не включаю Ах в Bh и я не 't передать что-нибудь на бар функция.

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

+0

Ищите для 'cyclic includes' и' forward declaration' – Thomas

+0

Циркулярное повторение. Подробнее здесь: http: //stackoverflow.com/questions/625799/resolve-circular-dependencies-in-c и здесь: http://stackoverflow.com/questions/17865286/c-circular-include и здесь: https://en.wikipedia.org/wiki/Circular_dependency. TL; DR: один из заголовков должен быть включен первым, и поскольку он включает в себя другой, другой не может включать первый, чтобы получить определения, которые ему нужны, поскольку первый уже включен. – user4581301

ответ

2

В заголовочном файле Ah, у вас есть:

#include "B.h" 

В файле заголовка Bh, у вас есть:

#include "A.h" 

Вы круговое включают где определение от A и B зависит друг от друга.


Простое решение заключается в использовании forward declaration в определении class B:

  1. Заменить строку #include "A.h" в файле B.h с class B;
  2. Добавить #include "A.h" в начале B.каст

Обратите внимание, однако, вы не можете использовать вперед декларацию для class A, причина в том, что у вас есть значение b из class B в качестве переменной-члена class A:

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b;    /// I mean this line here more specifically 
}; 

Необходимость компилятор чтобы узнать определение class B, чтобы определить правильный размер для класса A. Вот почему вы должны положить #include "B.h" в самом начале A.h.

+0

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

+0

Спасибо! Это сработало для меня. Я еще не знал о форвардной декларации :) – mrlux

1

Оба заголовка ссылаются друг на друга. Только один из них может быть оценен сначала компилятором и что невозможно решить ссылку на класс в другом заголовке. Ошибка не очевидна, но «некогда» # прагма делает включение одного из заголовков не так, как вы ожидаете.

Если заголовочный файл B.h не нуждается в описании деталей реализации класса A, чего нет в этом случае, то не следует включать файл A.h в B.h. Вместо этого, изменить объявление функции бара(), чтобы выглядеть следующим образом:

bar(class A *a); 

компилятор может создать код из этого, что передает указатель на объект, не зная ничего о том, что находится внутри А. Безразлично» t нужен заголовок Ah.

Затем укажите заголовочный файл A.h после заголовочного файла B.h в B.cpp.

Я тестировал это, и он работает для меня.

+0

Это работало для меня, чтобы использовать форвардное объявление A в начале B.h. В чем разница между этим и объявлением бара, как вы? Какая практика? – mrlux

+0

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

2

Давайте посмотрим на B.cpp и посмотреть, что происходит с включает в себя:

#include "B.h" 

B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

С Bh вставили вместо включаемого мы получаем:

#include "A.h" 

class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 


B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

А потом размещая Ah в место его включают в себя:

#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 


B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

И здесь мы останавливаемся, потому что pragma once предотвращает повторное в clusion of B.h

Мы можем видеть, что в определении класса A мы имеем B b;, но класс B еще не задан. Kaboom. A не может быть определен без B и B еще не определен.

A должен иметь размер B для удовлетворения B b;, поэтому для этого требуется полное определение B. B нужно знать только A, потому что ему нужен только указатель на A, чтобы удовлетворить void bar(A *a);. Так что ...

хиджры

#pragma once 
#include "B.h" 
class A 
{ 
public: 
    A(); 
    void foo(); 
    void sayHello(); 
    B b; 
}; 

А.каст

#include "A.h" 
#include <iostream>  
A::A() {} 
void A::foo() { 
    b.bar(this); 
} 
void A::sayHello() { 
    std::cout << "Hello" << std::endl; 
} 

B.h

#pragma once 

class A; // forward definition of class A 
class B 
{ 
public: 
    B(); 
    void bar(A *a); 
}; 

B.cpp

#include "A.h" 
B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 
+0

Спасибо за подробное объяснение, почему это происходит. Теперь я понимаю это лучше :) – mrlux

+0

@mrlux Спасибо, что дали мне возможность написать этот ответ с короткозначимыми заголовками. Хорошо составленный вопрос, кстати. – user4581301

0

эти изменения:

B.h : - Вперед объявить A

#pragma once 
// #include "A.h" 
class A; 
//^^^^^^ 

class B { 
public: 
    B(); 
    void bar(A *a); 
}; 

B.cpp: #include "A.h"

//#include "B.h" 
#include "A.h" 
//^^^^^^^^^^^^ 

B::B(){} 
void B::bar(A *a) { 
    a->sayHello(); 
} 

Вот и все.

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

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