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

Boris Jakovljevic picture Boris Jakovljevic · Feb 25, 2013 · Viewed 37.5k times · Source

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.

Don't torture yourself by reading the meat of this code; I just need the forward declaration and mutual inclusion problem 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

#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

#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 introForm inside QuizForm.

Thanks in advance! Boris J.

Answer

Kaslai picture Kaslai · Feb 25, 2013

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

  • 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 have to 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:

On codepad with the headers inline-included.

The individual files:

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:

#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:

#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;
}