C++错误:无效使用不完整类型...

8

我正在参加本学期的软件工程课程,有一个中小型项目需要完成。我选择使用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" 替换它,我会遇到头文件循环引用的问题。
我知道我发布了很多代码,但我不想因未发布足够的代码而受到攻击。

7
你发布了很多代码,但遗漏了关键的一部分... Login_Dialog.h。 - Naveen
3个回答

16

只要你在Login_Dialog.h中仅向类型声明一个指针(即你所做的),并将其添加到Login_Dialog.h的顶部,以便编译器知道稍后会看到一个类声明,那么你就可以使用前向声明MainWindow而不会出错。

class MainWindow;

然后在 Login_Dialog.cpp 中像这样包含 "mainwindow.h"。

/*
 * Login_Dialog.cpp
 *
 *  Created on: Mar 2, 2010
 *      Author: Matthew
 */

#include "Login_Dialog.h"
#include "MainWindow.h"

那就可以了。


2
+1 是一种更具体且易于理解的表述方式,比我之前尝试(但失败)表达的要好。 :P - Billy ONeal

1
当我尝试这样做时,我会得到本帖顶部呈现的错误。如果我尝试删除类MainWindow;并将其替换为#include "MainWindow.h",我会遇到头文件循环引用问题。
但这就是问题所在。您需要将实现移动到单独的实现(.cpp)文件中。您可以使用前向声明来打破头文件中的循环引用,但在尝试使用类型之前,必须同时拥有两个头文件。
您必须在使用类之前包含其完整定义--而不仅仅是前向声明。前向声明只对其他前向声明有用--编译器需要知道它正在处理哪种类型才能生成代码。

0
要修复错误,你应该将#include "Login_Dialog.h"替换为在Main_Window.h中对Login_Dialog类进行前向声明。然后在Main_Window.cpp中包含Login_Dialog.h,在Login_Dialog.cpp中包含Main_Window.h。顺便说一句,对于许多其他文件/类也可以做同样的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接