我有一个中小型项目,我正在为本学期的软件工程课程做。我选择用C ++(gtkmm)来做。到目前为止,我做得还不错,但我遇到了循环引用或以下错误的问题:
Login_Dialog.cpp:25: error: invalid use of incomplete type ‘struct MainWindow’
Login_Dialog.h:12: error: forward declaration of ‘struct MainWindow’
make: *** [Login_Dialog.o] Error 1
总之,我有大约10个班级,我知道将来他们都需要互相交谈。到目前为止,我遇到了一个特定的案例,我一直在努力解决这个问题,但我完全陷入困境。
我的程序有一个主窗口类,定义如下:
/*
* MainWindow.h
*/
#ifndef MAINWINDOW_H_
#define MAINWINDOW_H_
#include "includes.h"
#include "ModelDrawing.h"
#include "ViewDrawing.h"
#include "ControlerDrawing.h"
#include "ModelChat.h"
#include "ViewChat.h"
#include "ControlerChat.h"
#include "ModelQueue.h"
#include "ViewQueue.h"
#include "ControlerQueue.h"
#include "Login_Dialog.h"
#include "TCP_IP_Socket.h"
class MainWindow : public Window
{
public:
MainWindow(int type);
~MainWindow();
void on_menu_file_new_generic();
void on_menu_file_quit();
ModelDrawing* get_mdl_Draw();
ViewDrawing* get_view_Draw();
ControlerDrawing* get_cntrl_Draw();
ModelChat* get_mdl_Chat();
ViewChat* get_view_Chat();
ControlerChat* get_cntrl_Chat();
ModelQueue* get_mdl_Que();
ViewQueue* get_view_Que();
ControlerQueue* get_cntrl_Que();
Label* get_status_label();
void set_status_label(Glib::ustring label);
TCP_IP_Socket* get_socket();
private:
TCP_IP_Socket* socket;
Widget* menu;
RefPtr<Gtk::ActionGroup> m_refActionGroup;
RefPtr<Gtk::UIManager> m_refUIManager;
ModelDrawing* mdl_Draw;
ViewDrawing* view_Draw;
ControlerDrawing* cntrl_Draw;
ModelChat* mdl_Chat;
ViewChat* view_Chat;
ControlerChat* cntrl_Chat;
ModelQueue* mdl_Que;
ViewQueue* view_Que;
ControlerQueue* cntrl_Que;
Frame* label_frame;
Label* status_label;
Login_Dialog* login;
protected:
//Containers
HBox* main_HBox;
VBox* base_VBox;
};
#endif /* MAINWINDOW_H_ */
功能定义如下:
/*
* MainWindow.cpp
*/
#include "MainWindow.h"
MainWindow::MainWindow(int type)
{
this->socket = new TCP_IP_Socket(this);
//Login Section
this->login = new Login_Dialog(WINDOW_TOPLEVEL, this);
int status;
status = this->login->run();
if(status == 0)
{
exit(1);
}
this->login->hide();
//By Default Create and Open Up Student Queue
this->mdl_Que = new ModelQueue(this);
this->view_Que = new ViewQueue(this);
this->cntrl_Que = new ControlerQueue(this, (this->mdl_Que), (this->view_Que));
this->set_default_size(1200, 750);
this->set_border_width(1);
this->set_title("Tutor App");
this->base_VBox = manage(new VBox());
this->main_HBox = manage(new HBox());
this->label_frame = manage(new Frame());
m_refActionGroup = Gtk::ActionGroup::create();
m_refUIManager = Gtk::UIManager::create();
m_refActionGroup->add(Gtk::Action::create("FileMenu", "File"));
this->add_accel_group(m_refUIManager->get_accel_group());
Glib::ustring ui_info =
"<ui>"
"<menubar name='MenuBar'>"
" <menu action='FileMenu'>"
" </menu>"
"</menubar>"
"</ui>";
m_refUIManager->insert_action_group(m_refActionGroup);
m_refUIManager->add_ui_from_string(ui_info);
this->menu = m_refUIManager->get_widget("/MenuBar");
this->mdl_Draw = new ModelDrawing(this);
this->view_Draw = new ViewDrawing(this);
this->cntrl_Draw = new ControlerDrawing(this, (this->mdl_Draw), (this->view_Draw));
this->mdl_Chat = new ModelChat(this);
this->view_Chat = new ViewChat(this);
this->cntrl_Chat = new ControlerChat(this, (this->mdl_Chat), (this->view_Chat));
this->status_label = manage(new Label("Welcome to The Tutor App", ALIGN_LEFT, ALIGN_LEFT, false));
//Put it all together
this->main_HBox->pack_start(*(this->view_Draw->get_left_VBox()));
this->label_frame->add(*(this->status_label));
this->base_VBox->pack_end(*(this->label_frame));
this->main_HBox->pack_end(*(this->view_Chat->get_right_VBox()));
this->base_VBox->pack_start(*(this->menu), Gtk::PACK_SHRINK);
this->base_VBox->pack_end(*(this->main_HBox), true, true);
this->label_frame->set_size_request(-1, 5);
this->add(*(this->base_VBox));
this->show_all();
this->view_Que->get_window()->show_all();
}
MainWindow::~MainWindow()
{
}
ModelDrawing* MainWindow::get_mdl_Draw()
{
return NULL;
}
ViewDrawing* MainWindow::get_view_Draw()
{
return NULL;
}
ControlerDrawing* MainWindow::get_cntrl_Draw()
{
return NULL;
}
ModelChat* MainWindow::get_mdl_Chat()
{
return NULL;
}
ViewChat* MainWindow::get_view_Chat()
{
return NULL;
}
ControlerChat* MainWindow::get_cntrl_Chat()
{
return NULL;
}
ModelQueue* MainWindow::get_mdl_Que()
{
return NULL;
}
ViewQueue* MainWindow::get_view_Que()
{
return this->view_Que;
}
ControlerQueue* MainWindow::get_cntrl_Que()
{
return NULL;
}
Label* MainWindow::get_status_label()
{
return this->status_label;
}
void MainWindow::set_status_label(Glib::ustring label)
{
this->status_label->set_label(label);
}
TCP_IP_Socket* MainWindow::get_socket()
{
return this->socket;
}
void MainWindow::on_menu_file_quit()
{
hide(); //Closes the main window to stop the Gtk::Main::run().
}
void MainWindow::on_menu_file_new_generic()
{
std::cout << "A File|New menu item was selected." << std::endl;
}
现在主窗口创建一个TCP_IP_Socket
类和一个登录对话框。我首先创建连接并设置几个字符串(见下面的代码):
/*
* TCP_IP_Socket.cpp
*/
#include "TCP_IP_Socket.h"
TCP_IP_Socket::TCP_IP_Socket(MainWindow* hwnd)
{
this->hwnd = hwnd;
server_name = "www.geoginfo.com";
this->set_server_ustring(this->server_name);
printf("%s", this->server_name);
struct addrinfo specs;
struct addrinfo* results;
int status;
memset(&specs, 0, sizeof specs);
specs.ai_flags = 0;
specs.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
specs.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(this->server_name, NULL, &specs, &results)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
exit(0);
}
char ipstr[INET6_ADDRSTRLEN];
void* addr;
if (results->ai_family == AF_INET)
{ // IPv4
struct sockaddr_in* ipv4 = (struct sockaddr_in*)results->ai_addr;
addr = &(ipv4->sin_addr);
}
else
{ // IPv6
struct sockaddr_in6* ipv6 = (struct sockaddr_in6 *)results->ai_addr;
addr = &(ipv6->sin6_addr);
}
inet_ntop(results->ai_family, addr, ipstr, sizeof ipstr);
this->set_serverip_ustring(ipstr);
printf(" = %s\n", ipstr);
freeaddrinfo(results); // free the linked list
printf("\n");
}
TCP_IP_Socket::~TCP_IP_Socket()
{
}
void TCP_IP_Socket::set_server_ustring(const char* server_name)
{
this->server_domainname = new ustring(server_name);
}
void TCP_IP_Socket::set_serverip_ustring(const char* ip)
{
this->server_ip = new ustring(ip);
}
Glib::ustring* TCP_IP_Socket::get_server_domainname()
{
return this->server_domainname;
}
Glib::ustring* TCP_IP_Socket::get_server_ip()
{
return this->server_ip;
}
然后我创建了登录名,并尝试从我的登录对话框中访问server_ip
ustring和server_domainname
ustring:
/*
* Login_Dialog.cpp
*/
#include "Login_Dialog.h"
Login_Dialog::Login_Dialog(int type, MainWindow* hwnd)
{
this->hwnd = hwnd;
this->set_default_size(100, 150);
this->user_layout = manage(new HBox());
this->pswd_layout = manage(new HBox());
this->user_name = manage(new Label("Username"));
this->user_entry = manage(new Entry());
this->pswd_user = manage(new Label("Password"));
this->pswd_entry = manage(new Entry());
this->Ok = add_button("Ok", 1);
this->Cancel = add_button("Cancel", 0);
Glib::ustring* one = hwnd->get_socket()->get_server_domainname();
this->status_label = manage (new Label("This is a test", ALIGN_LEFT, ALIGN_LEFT, false));
this->Ok->set_size_request(74, -1);
this->Cancel->set_size_request(74, -1);
this->user_layout->pack_start(*(this->user_name), true, true);
this->user_layout->pack_end(*(this->user_entry), true, true);
this->pswd_layout->pack_start(*(this->pswd_user), true, true);
this->pswd_layout->pack_end(*(this->pswd_entry), true, true);
this->get_vbox()->pack_start(*(this->user_layout));
this->get_vbox()->pack_end(*(this->status_label), true, true);
this->get_vbox()->pack_end(*(this->pswd_layout));
show_all(); //<-- This is key
}
void Login_Dialog::set_status_label(Glib::ustring label)
{
this->status_label->set_label(label);
}
当我尝试编译这个时,我得到了这篇文章最上面列出的错误。如果我删除class MainWindow;
并将其替换为#include "MainWindow.h"
,则会遇到标题的循环引用问题。
我知道我发布了很多代码,但我不想因为没有足够的帖子而受到抨击。
答案 0 :(得分:15)
只要您只转发声明指向该类型的指针(您执行此操作),就可以在Login_Dialog.h中声明前向声明MainWindow,然后将其添加到Login_Dialog.h
的顶部,以便编译器知道期待稍后看到一个班级声明。
class MainWindow;
然后在Login_Dialog.cpp中,像这样包含“mainwindow.h”。
/*
* Login_Dialog.cpp
*
* Created on: Mar 2, 2010
* Author: Matthew
*/
#include "Login_Dialog.h"
#include "MainWindow.h"
应该这样做。
答案 1 :(得分:1)
当我尝试这样做时,我会在本文的最顶部看到错误。如果我尝试删除MainWindow类;并用#include“MainWindow.h”替换它。我遇到了标题的循环引用问题。
但这就是问题所在。您需要将实现移动到单独的实现(.cpp)文件中。您可以使用前向声明来破坏头文件中的循环引用,但在尝试使用类型之前必须同时具有两个头。
在使用之前,您必须包含您的类的完整定义 - 而不仅仅是前向声明。前向声明仅对其他前向声明有用 - 编译器需要知道它在生成代码之前使用的类型。
答案 2 :(得分:0)
要修复错误,您应该将#include“Login_Dialog.h”替换为Main_Window.h中的Login_Dialog类的前向声明。然后在Main_Window.cpp中包含Login_Dialog.h,在Login_Dialog.cpp中包含Main_Window.h。顺便说一句,对许多其他文件/类也可以这样做。