C++ 前向声明和“不允许不完整的类型”错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15076026/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:59:32  来源:igfitidea点击:

C++ Forward declaration and 'Incomplete type is not allowed' error

c++c++-clideclarationforwardincomplete-type

提问by Boris Jakovljevic

I have two classes (or better yet, header files) that are part of my C++ program and I simply can't make this all work! They basically need each other's data in order to function properly, as part of my app.

我有两个类(或者更好的是头文件),它们是我的 C++ 程序的一部分,我根本无法使这一切正常工作!作为我的应用程序的一部分,它们基本上需要彼此的数据才能正常运行。

Don't torture yourself by reading the meat of this code; I just need the forward declarationand mutual inclusionproblem resolved, so only take a look at those parts of the code which matter for this problem.

不要通过阅读这段代码的内容来折磨自己;我只需要解决前向声明相互包含的问题,所以只看代码中对这个问题很重要的那些部分。

The error occurs in the second code snippet, at the very end (I had to chop off a huge chunk of code from the first snippet, so I could post the question, I guess it's irrelevant to my problem).

错误发生在第二个代码片段的最后(我不得不从第一个片段中剪掉一大块代码,所以我可以发布问题,我想这与我的问题无关)。

introForm.h

introForm.h

#pragma once
#include <stdlib.h>
#include "creditsForm.h"
#include "registerForm.h"
#include "aboutForm.h"
#include "QuizForm.h"
#include <windows.h>
#include <time.h>
#ifndef __introForm__H__
#define __introForm__H__

namespace InteractiveQuiz {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    ref class QuizForm;

    /// <summary>
    /// Summary for introForm
    /// </summary>
    public ref class introForm : public System::Windows::Forms::Form
    {
    public:
        introForm(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~introForm()
        {
            if (components)
            {
                delete components;
            }
        }

        // Extracts a word from a sentence with a given ordinal number
        string extractWord(int ordinal, string sentence)
        {
            int counter = 0;
            string word;

            for (int i = 0; i < sentence.length(); i++)
            {
                if (sentence[i] == ',')
                {
                    if (sentence [i+1] != ',')
                    {
                        counter ++;

                        if (counter == ordinal)
                        {
                            return word;
                        }

                        word ="";
                    }
                }
                else
                {
                    word += sentence[i];
                }       
            }
        }

private: System::Void btnExit1_Click(System::Object^  sender, System::EventArgs^  e) {
             exit(1);
         }
private: System::Void btnCredits1_Click(System::Object^  sender, System::EventArgs^  e) {
             creditsForm^ credits = gcnew creditsForm;
             credits->Show();
         }
private: System::Void registerBtn_Click(System::Object^  sender, System::EventArgs^  e) {
             registerForm^ registerUser = gcnew registerForm;
             registerUser->Show();
         }
private: System::Void aboutToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e) {
             aboutForm^ aboutApp = gcnew aboutForm;
             aboutApp->Show();
         }
private: System::Void quitToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e) {
             introForm::Close();
         }
private: System::Void introForm_Load(System::Object^  sender, System::EventArgs^  e) {
             ifstream regUsers ("regUsers.csv");

             if (regUsers)
             {
                 lblUser->Enabled = true;
                 lblPass->Enabled = true;

                 logUsername->Enabled = true;
                 logPassword->Enabled = true;

                 btnLogSubmit->Enabled = true;
                 if (lblLogin->Text == L"Not logged in")
                 {
                    quizLogo->Visible = false;
                 }
             }
             else
             {
                 lblUser->Enabled = false;
                 lblPass->Enabled = false;

                 logUsername->Enabled = false;
                 logPassword->Enabled = false;

                 btnLogSubmit->Enabled = false;

                 quizLogo->Visible = true;
             }

             regUsers.close();
         }

public: String^ loggedUser;

private: System::Void btnLogSubmit_Click(System::Object^  sender, System::EventArgs^  e) {
             if (logUsername->Text->Length < 6 && logPassword->Text->Length < 8)
             {
                 errorLabel->Text = L"Username must be at least 6 and password 8 characters long!";
                 errorLabel->Visible = true;
             }
             else if (logUsername->Text->Length < 6)
             {
                 errorLabel->Text = L"Username must be at least 6 characters long!";
                 errorLabel->Visible = true;
             }
             else if (logUsername->Text->Contains(" "))
             {
                 errorLabel->Text = L"Username must not contain spaces!";
                 errorLabel->Visible = true;
             }
             else if (logPassword->Text->Length < 8)
             {
                 errorLabel->Text = L"Password must be at least 8 characters long!";
                 errorLabel->Visible = true;
             }
             if (logUsername->Text->Length >= 6 && logPassword->Text->Length >= 8)
             {
                 String^ result = logUsername->Text + "," + logPassword->Text;
                 string result2 = marshal_as<string>(result);

                 ifstream regUsers("regUsers.csv");
                 string line;
                 string fileUserPass;

                 /* While there is still a line. */
                 while(getline(regUsers, line))
                 {
                     fileUserPass = extractWord(1, line) + "," + extractWord(2,line);

                     if (fileUserPass == result2) // Successful login
                     {
                         lblLogin->Text = L"Logged in as " + logUsername->Text;

                         quizLogo->Visible = true;
                         errorLabel->Visible = false;

                         btnLogout->Enabled = true;

                         startNewToolStripMenuItem->Enabled = true;

                         loggedUser = logUsername->Text;
                     } 
                 }

                 regUsers.close();

                 if (fileUserPass != result2)
                 {
                     errorLabel->Text = L"Username or password incorrect!";
                     errorLabel->Visible = true;
                 }
             }
         }
private: System::Void btnLogout_Click(System::Object^  sender, System::EventArgs^  e) {
             btnLogout->Enabled = false;

             lblLogin->Text = L"Not logged in";

             quizLogo->Visible = false;

             errorLabel->Visible = false;

             logUsername->Clear();
             logPassword->Clear();

             logUsername->Focus();

             startNewToolStripMenuItem->Enabled = false;
         }
private: System::Void startNewToolStripMenuItem_Click(System::Object^  sender, System::EventArgs^  e) {
             QuizForm^ quiz = gcnew QuizForm;
             quiz->Show();
         }
};
}

#endif // !__introForm__H__

QuizForm.h

测验表格.h

#pragma once
#include <string.h>
using namespace std;

#ifndef __QuizForm__H__
#define __QuizForm__H__

namespace InteractiveQuiz {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    ref class introForm;

    /// <summary>
    /// Summary for QuizForm
    /// </summary>
    public ref class QuizForm : public System::Windows::Forms::Form
    {
    public:
        QuizForm(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }


    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~QuizForm()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::TabControl^  tabControl;
    protected: 

    protected: 
    private: System::Windows::Forms::TabPage^  tabPage1;
    private: System::Windows::Forms::TabPage^  tabPage2;
    private: System::Windows::Forms::MenuStrip^  menuStrip1;
    private: System::Windows::Forms::Button^  button1;
    private: System::Windows::Forms::ToolStripMenuItem^  quizToolStripMenuItem;
    private: System::Windows::Forms::ToolStripMenuItem^  startNewToolStripMenuItem;
    private: System::Windows::Forms::ToolStripMenuItem^  endQuizToolStripMenuItem;
    private: System::Windows::Forms::ToolStripSeparator^  toolStripSeparator1;
    private: System::Windows::Forms::ToolStripMenuItem^  quitToolStripMenuItem;
    private: System::Windows::Forms::ToolStripMenuItem^  helpToolStripMenuItem;
    private: System::Windows::Forms::ToolStripMenuItem^  aboutToolStripMenuItem;
    private: System::Windows::Forms::Label^  lblQuizLogin;


    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->tabControl = (gcnew System::Windows::Forms::TabControl());
            this->tabPage1 = (gcnew System::Windows::Forms::TabPage());
            this->tabPage2 = (gcnew System::Windows::Forms::TabPage());
            this->menuStrip1 = (gcnew System::Windows::Forms::MenuStrip());
            this->quizToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->startNewToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->endQuizToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->toolStripSeparator1 = (gcnew System::Windows::Forms::ToolStripSeparator());
            this->quitToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->helpToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->aboutToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
            this->button1 = (gcnew System::Windows::Forms::Button());
            this->lblQuizLogin = (gcnew System::Windows::Forms::Label());
            this->tabControl->SuspendLayout();
            this->menuStrip1->SuspendLayout();
            this->SuspendLayout();
            // 
            // tabControl
            // 
            this->tabControl->Controls->Add(this->tabPage1);
            this->tabControl->Controls->Add(this->tabPage2);
            this->tabControl->Location = System::Drawing::Point(12, 27);
            this->tabControl->Name = L"tabControl";
            this->tabControl->SelectedIndex = 0;
            this->tabControl->Size = System::Drawing::Size(686, 430);
            this->tabControl->TabIndex = 0;
            // 
            // tabPage1
            // 
            this->tabPage1->Location = System::Drawing::Point(4, 22);
            this->tabPage1->Name = L"tabPage1";
            this->tabPage1->Padding = System::Windows::Forms::Padding(3);
            this->tabPage1->Size = System::Drawing::Size(678, 404);
            this->tabPage1->TabIndex = 0;
            this->tabPage1->Text = L"tabPage1";
            this->tabPage1->UseVisualStyleBackColor = true;
            // 
            // tabPage2
            // 
            this->tabPage2->Location = System::Drawing::Point(4, 22);
            this->tabPage2->Name = L"tabPage2";
            this->tabPage2->Padding = System::Windows::Forms::Padding(3);
            this->tabPage2->Size = System::Drawing::Size(678, 404);
            this->tabPage2->TabIndex = 1;
            this->tabPage2->Text = L"tabPage2";
            this->tabPage2->UseVisualStyleBackColor = true;
            // 
            // menuStrip1
            // 
            this->menuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^  >(2) {this->quizToolStripMenuItem, 
                this->helpToolStripMenuItem});
            this->menuStrip1->Location = System::Drawing::Point(0, 0);
            this->menuStrip1->Name = L"menuStrip1";
            this->menuStrip1->Size = System::Drawing::Size(710, 24);
            this->menuStrip1->TabIndex = 1;
            this->menuStrip1->Text = L"menuStrip1";
            // 
            // quizToolStripMenuItem
            // 
            this->quizToolStripMenuItem->DropDownItems->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^  >(4) {this->startNewToolStripMenuItem, 
                this->endQuizToolStripMenuItem, this->toolStripSeparator1, this->quitToolStripMenuItem});
            this->quizToolStripMenuItem->Name = L"quizToolStripMenuItem";
            this->quizToolStripMenuItem->Size = System::Drawing::Size(43, 20);
            this->quizToolStripMenuItem->Text = L"&Quiz";
            // 
            // startNewToolStripMenuItem
            // 
            this->startNewToolStripMenuItem->Name = L"startNewToolStripMenuItem";
            this->startNewToolStripMenuItem->Size = System::Drawing::Size(125, 22);
            this->startNewToolStripMenuItem->Text = L"Start &New";
            // 
            // endQuizToolStripMenuItem
            // 
            this->endQuizToolStripMenuItem->Name = L"endQuizToolStripMenuItem";
            this->endQuizToolStripMenuItem->Size = System::Drawing::Size(125, 22);
            this->endQuizToolStripMenuItem->Text = L"&End Quiz";
            // 
            // toolStripSeparator1
            // 
            this->toolStripSeparator1->Name = L"toolStripSeparator1";
            this->toolStripSeparator1->Size = System::Drawing::Size(122, 6);
            // 
            // quitToolStripMenuItem
            // 
            this->quitToolStripMenuItem->Name = L"quitToolStripMenuItem";
            this->quitToolStripMenuItem->Size = System::Drawing::Size(125, 22);
            this->quitToolStripMenuItem->Text = L"Q&uit";
            // 
            // helpToolStripMenuItem
            // 
            this->helpToolStripMenuItem->DropDownItems->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^  >(1) {this->aboutToolStripMenuItem});
            this->helpToolStripMenuItem->Name = L"helpToolStripMenuItem";
            this->helpToolStripMenuItem->Size = System::Drawing::Size(44, 20);
            this->helpToolStripMenuItem->Text = L"&Help";
            // 
            // aboutToolStripMenuItem
            // 
            this->aboutToolStripMenuItem->Name = L"aboutToolStripMenuItem";
            this->aboutToolStripMenuItem->Size = System::Drawing::Size(116, 22);
            this->aboutToolStripMenuItem->Text = L"&About...";
            // 
            // button1
            // 
            this->button1->Location = System::Drawing::Point(623, 476);
            this->button1->Name = L"button1";
            this->button1->Size = System::Drawing::Size(75, 23);
            this->button1->TabIndex = 2;
            this->button1->Text = L"Close";
            this->button1->UseVisualStyleBackColor = true;
            // 
            // lblQuizLogin
            // 
            this->lblQuizLogin->AutoSize = true;
            this->lblQuizLogin->Location = System::Drawing::Point(12, 489);
            this->lblQuizLogin->Name = L"lblQuizLogin";
            this->lblQuizLogin->Size = System::Drawing::Size(70, 13);
            this->lblQuizLogin->TabIndex = 3;
            this->lblQuizLogin->Text = L"Not logged in";
            // 
            // QuizForm
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(710, 511);
            this->Controls->Add(this->lblQuizLogin);
            this->Controls->Add(this->button1);
            this->Controls->Add(this->tabControl);
            this->Controls->Add(this->menuStrip1);
            this->MainMenuStrip = this->menuStrip1;
            this->MaximizeBox = false;
            this->MinimizeBox = false;
            this->Name = L"QuizForm";
            this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
            this->Text = L"Interactive Quiz";
            this->Load += gcnew System::EventHandler(this, &QuizForm::QuizForm_Load);
            this->tabControl->ResumeLayout(false);
            this->menuStrip1->ResumeLayout(false);
            this->menuStrip1->PerformLayout();
            this->ResumeLayout(false);
            this->PerformLayout();

        }
#pragma endregion

private: System::Void QuizForm_Load(System::Object^  sender, System::EventArgs^  e) {
             introForm^ intro = gcnew introForm; // the second introForm in this line is underlined with the mentioned error
         }
    };
}

#endif // !__QuizForm__H__

What you see in these 2 pieces of code is something I constructed as puzzle pieces I found throughout the net, haha. :)

你在这两段代码中看到的是我在网上找到的拼图,哈哈。:)

My goal:to be able to use a variable from introForminside QuizForm.

我的目标:能够在QuizForm 中使用来自introForm的变量。

Thanks in advance! Boris J.

提前致谢!鲍里斯 J。

回答by Kaslai

When using forward declarations of objects in C++, keep a few things in mind.

在 C++ 中使用对象的前向声明时,请记住一些事项。

  • You cannot allocate an incomplete type, as the compiler doesn't know how big it is yet. This is solved by only using pointers to incomplete types.
  • You cannot reference the members of an incomplete type before it's definition is seen by the compiler. This is solved by separating your interface and implementation in to header and code files.
  • If you are in a situation where you haveto use an incomplete type, you're probably doing something wrong. Try to avoid circular references wherever possible.
  • 您不能分配不完整的类型,因为编译器还不知道它有多大。这是通过仅使用指向不完整类型的指针来解决的。
  • 在编译器看到定义之前,您不能引用不完整类型的成员。这是通过将您的接口和实现分离到头文件和代码文件来解决的。
  • 如果您处于必须使用不完整类型的情况,那么您可能做错了什么。尽量避免循环引用。

In regards to your comment:

关于你的评论:

When you have a forward declaration of a class, like class foo;, it's called an incomplete type, as the definition doesn't exist yet. You can refer to it indirectly via pointers, like foo *bar;, but you can't use it in any operations that require knowledge of its internals, such as dereferencing it, using functions that it contains, etc. You can make a full declaration of a class, though using function prototypes instead of functions. You can then define the functions in another file. This will allow you to completely define a circular dependency without making anything blow up. For example:

当您有一个类的前向声明时,例如class foo;,它被称为不完整类型,因为定义尚不存在。您可以通过指针间接引用它,例如foo *bar;,但不能在需要了解其内部结构的任何操作中使用它,例如取消引用它、使用它包含的函数等。您可以对类进行完整声明,虽然使用函数原型而不是函数。然后您可以在另一个文件中定义函数。这将允许您完全定义循环依赖,而不会造成任何问题。例如:

On codepad with the headers inline-included.

在带有内联标题的键盘上。

The individual files:

个别文件:

foo.h:

foo.h:

#ifndef __FOO_H_INCLUDE
    #define __FOO_H_INCLUDE
    #ifndef __FOO_H_DEFINED
        #define __FOO_H_DEFINED
        class foo;
        #include "bar.h"
    #endif
    class foo{
        bar *a;
        int b;
        public:
        foo( int _b );
        bar& getbar();
        int getb();
        void setbar( bar* _a );
    };
#endif

bar.h:

酒吧.h:

#ifndef __BAR_H_INCLUDE
    #define __BAR_H_INCLUDE
    #ifndef __BAR_H_DEFINED
        #define __BAR_H_DEFINED
        class bar;
        #include "foo.h"
    #endif
    class bar{
        foo *a;
        int b;
        public:
        bar(int _b );
        foo& getfoo();
        int getb();
        void setfoo( foo* _a );
    };
#endif

main.cpp:

主.cpp:

#include<iostream>
#include "foo.h"
#include "bar.h"

foo::foo( int _b ){ b = _b;}
int foo::getb(){ return b; }
bar& foo::getbar(){ return *a; }
void foo::setbar( bar* _a){ a = _a; }

bar::bar( int _b ){ b = _b;}
int bar::getb(){ return b; }
foo& bar::getfoo(){ return *a; }
void bar::setfoo( foo* _a ){ a = _a; }



int main(){
    foo a(5);
    bar b(20);
    a.setbar(&b);
    b.setfoo(&a);
    std::cout   << a.getbar().getfoo().getbar().getb() 
                << "\n" 
                << a.getbar().getfoo().getbar().getfoo().getb();
    return 0;
}