代码之家  ›  专栏  ›  技术社区  ›  Matthew Hoggan

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

c++
  •  7
  • Matthew Hoggan  · 技术社区  · 14 年前

    我有一个中小规模的项目,我正在为我的软件工程课程这学期。我选择用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 美化和 server_domainname 从我的登录对话框中调用:

    /*
     * 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" ,我遇到了头的循环引用问题。

    我知道我发了很多代码,但我不想因为发得不够多而生气。

    3 回复  |  直到 13 年前
        1
  •  15
  •   John Knoeller    14 年前

    在login_dialog.h中,只要您只转发一个指向类型的指针(您这样做),并将其添加到 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"
    

    应该可以。

        2
  •  1
  •   Billy ONeal IS4    14 年前

    当我试着这样做时,我会在这篇文章的最顶端显示错误。如果我尝试删除类mainwindow;并用include“mainwindow.h”替换它,我会遇到带有头的循环引用问题。

    但这就是问题所在。您需要将实现移动到单独的实现(.cpp)文件中。可以使用前向声明来中断头文件中的循环引用,但在尝试使用类型之前,必须使两个头都可用。

    在使用类之前,必须包含类的完整定义——而不仅仅是转发声明。前向声明只对其他前向声明有用——编译器在生成代码之前需要知道它使用的是什么类型。

        3
  •  0
  •   Corwin    14 年前

    若要修复此错误,应将“include“login_dialog.h”替换为main_window.h中login_dialog类的前向声明,然后在main_window.cpp中包含login_dialog.h,在login_dialog.cpp中包含main_window.h。顺便说一下,其他许多文件/类也可以这样做。