Ссылка на другую структуру в структуре, где вторая структура также ссылается на первую - PullRequest
0 голосов
/ 27 марта 2012

Редактировать: прочитать строки, выделенные жирным шрифтом, чтобы найти, где у меня проблемы. После прочтения ответов я изменил код, но все равно получаю сообщение об ошибке

Я новичок в C ++, и я тестирую некоторые вещи, чтобы узнать, как использовать язык, и я столкнулся с этим случаем:

У меня есть struct, который создает указатель на другой struct. Этот другой struct, в свою очередь, создает указатель на struct, который его создал в первую очередь.

Вот быстрый пример, чтобы объяснить, что я имею в виду, немного лучше (эта часть была изменена с добавлением кода с жирным шрифтом, где у меня возникают проблемы):

#include <string>

using namespace std;

struct Month;
struct Year;

struct Day{
    unsigned int day, dayNumber;
    string name;

    Day(){}

    void setDayNumber(unsigned int dayNumber){
        this -> dayNumber = dayNumber;

        switch(dayNumber){
        case 0:
            name = "Sunday";
            break;
        case 1:
            name = "Monday";
            break;
        case 2:
            name = "Tuesday";
            break;
        case 3:
            name = "Wednesday";
            break;
        case 4:
            name = "Thursday";
            break;
        case 5:
            name = "Friday";
            break;
        case 6:
            name = "Saturday";
            break;
        }
    }
};

struct Month{
    string name;
    unsigned int monthLength, monthNumber;
    Month *previousMonth, *nextMonth;
    Year *year;
    Day *days;

    Month(unsigned int monthNumber, Year *year){
        this -> monthNumber = monthNumber;

        switch(monthNumber){
        case 0:
            name = "January";
            break;
        case 1:
            name = "February";
            break;
        case 2:
            name = "March";
            break;
        case 3:
            name = "April";
            break;
        case 4:
            name = "May";
            break;
        case 5:
            name = "June";
            break;
        case 6:
            name = "July";
            break;
        case 7:
            name = "August";
            break;
        case 8:
            name = "September";
            break;
        case 9:
            name = "October";
            break;
        case 10:
            name = "November";
            break;
        case 11:
            name = "December";
            break;
        }

        previousMonth = NULL;
        nextMonth = NULL;
        this -> year = year;
    }

    void createDays(){
        if(name == "January" || name == "March" || name == "May" || name == "July" || name == "August" || name == "October" || name == "December")
            monthLength = 31;
        else if(name != "February")
            monthLength = 30;
        else{
            **if(year -> isLeapYear() == true)** //This line is problematic, see below the code to see what the compiler says
                monthLength = 29;
            else 
                monthLength = 28;
        }

        days = new Day[monthLength];

        for(unsigned int i = 0; i < monthLength; i++){
            days[i].day = i+1;

            if(i == 0 && previousMonth == NULL)
                days[i].setDayNumber(2);
            else if(i == 0)
                days[i].setDayNumber(previousMonth -> days[previousMonth -> monthLength - 1].dayNumber);
            else
                days[i].setDayNumber(days[i-1].dayNumber);
        }
    }
};

struct Year{
    unsigned int year;
    Year *previousYear, *nextYear;
    Month *months;

    Year(unsigned int year){
        this -> year = year;
        previousYear = NULL;
        nextYear = NULL;
    }

    void createMonths(){
        Month *currentMonth = months;
        unsigned int monthNumber;

        for(unsigned int i = 0; i < 12; i++){
            months = new Month(i, this);
        }

        while(currentMonth != NULL){
            monthNumber = currentMonth -> monthNumber;

            if(monthNumber == 0 && previousYear != NULL){
                months[monthNumber].previousMonth = &(previousYear -> months[11]);
                months[monthNumber].nextMonth = &(months[monthNumber+1]);
            }
            else if(monthNumber == 11 && nextYear != NULL){
                months[monthNumber].nextMonth = &(nextYear -> months[0]);
                months[monthNumber].previousMonth = &(months[monthNumber-1]);
            }
            else{
                if(monthNumber != 0)
                    months[monthNumber].previousMonth = &(months[monthNumber-1]);
                if(monthNumber != 11)
                    months[monthNumber].nextMonth = &(months[monthNumber+1]);
            }
        }

        currentMonth = months;

        while(currentMonth != NULL){
            currentMonth -> createDays();
            currentMonth = currentMonth -> nextMonth;
        }
    }

    bool isLeapYear(){
        if(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
            return true;
        else
            return false;
    }
};

Вот сообщения, которые выводит компилятор:

pe_019.cpp(107): error C2027: use of undefined type 'Year'
pe_019.cpp(10) : see declaration of 'Year'
pe_019.cpp(107): error C2227: left of '->isLeapYear' must point to class/struct/union/generic type

Очевидно, что в этом есть нечто большее, но в Year я использую массив struct Month, а в Month есть указатель на его родительский Year. (То же самое для Day, имеет родителя Month).

Когда я пытаюсь скомпилировать, компилятор говорит, что я использую неопределенный struct 'Month', что Month* имеет неизвестный размер и т. Д.

Я почти уверен, что это потому, что Month используется до того, как оно объявлено, но я не могу сначала объявить его, потому что тогда Year будет необъявленным struct.

Как правильно это сделать?

Редактировать: После того, как я прочитал ответы на этот вопрос, я объявил год и поставил месяц до года, но у меня все еще возникают проблемы из-за функции, которую я не включил в свой первоначальный пост с тех пор Я не думал, что это было частью проблемы, я отредактировал свой код, чтобы сделать его более полным.

Ответы [ 5 ]

2 голосов
/ 27 марта 2012
struct Year;

struct Month{
    string name;
    unsigned int monthLength;
    struct Month *previousMonth, *nextMonth;
    struct Year *year;
    struct Day *days;
};

struct Year{
    unsigned int year;
    struct Year *previousYear, *nextYear;
    struct Month months[12];
};

Так и должно быть. Поскольку Month использует только указатель на Year, ему не нужно знать точный размер, и поэтому Year не обязательно должен быть полным типом до объявления Month s. Однако Year использует массив (нединамический) Month, поэтому он должен знать Month s точный размер.


Изменить после того, как ОП отредактировал вопрос:

Проблему, с которой вы столкнулись, легко решить. http://ideone.com/n4NYy - здесь ваш код скопирован и скомпилирован; как видите, проблема в том, что Year является неполным типом в момент реализации функции; просто переместите его за пределы объявления структуры, после объявления Year. Это одна из причин, по которой нельзя хранить объявления и реализации вместе в теле struct / class в C ++.

2 голосов
/ 27 марта 2012

Сначала переместите определение Month и объявите вперед Year.

struct Year;
struct Day;

struct Month{
    string name;
    unsigned int monthLength;
    struct Month *previousMonth, *nextMonth;
    struct Year *year;
    struct Day *days;
}

struct Year{
    unsigned int year;
    struct Year *previousYear, *nextYear;
    struct Month months[12];
}

Для массива месяцев Year необходимо полное определение Month.

В Month у вас есть только указатель на Year, поэтому достаточно предварительного объявления.

Обратите внимание, что в C ++ ключевое слово struct является избыточным - вы можете заменить, например, struct Year *year; на Year *year;.

2 голосов
/ 27 марта 2012

Ваш код может быть улучшен несколькими способами, как указано ниже.

#include <string>
using std::string;

struct Year;
struct Day;

struct Month{
    string name;
    unsigned int monthLength;
    struct Month *previousMonth, *nextMonth;
    struct Year *year;
    struct Day *days;
};

struct Year{
    unsigned int year;
    struct Year *previousYear, *nextYear;
    struct Month months[12];
};

Самая важная проблема, которую это исправляет, заключается в том, что, хотя в определении Месяц указан только указатель на Год, определение Год на самом деле включает в себя целые Месяцы. Давайте подумаем об этом на мгновение, потому что здесь есть что-то существенное для изучения.Если компилятор еще не знает, что такое Month , как он может решить, как разместить Year в памяти?Ответ: не может, потому что он даже не знает размер Месяца.

Простое включение указателя не приносит этой проблемы, потому что компилятор знает размер указателя, даже еслион ничего не знает о типе объекта, на который указывает объект.

Вашему коду также потребовалось несколько точек с запятой, а также стандартное включение заголовка, оба из которых я добавил.

Удачи.

Обновление в ответ на комментарий ОП ниже: Пересмотренный код размещен ниже.Обратите внимание, что и как, это задерживает определение уязвимой функции до тех пор, пока тип, в котором нуждается функция, не будет полностью определен.

(Кстати, если мои мысли по этому вопросу вас интересуют, с использованием пространства имен std почти никогда не рекомендуется. Он побеждает полезный механизм пространства имен C ++.)

#include <string>

using namespace std;

struct Month;
struct Year;

struct Day{
    unsigned int day, dayNumber;
    string name;

    Day(){}

    void setDayNumber(unsigned int dayNumber){
        this -> dayNumber = dayNumber;

        switch(dayNumber){
        case 0:
            name = "Sunday";
            break;
        case 1:
            name = "Monday";
            break;
        case 2:
            name = "Tuesday";
            break;
        case 3:
            name = "Wednesday";
            break;
        case 4:
            name = "Thursday";
            break;
        case 5:
            name = "Friday";
            break;
        case 6:
            name = "Saturday";
            break;
        }
    }
};

struct Month{
    string name;
    unsigned int monthLength, monthNumber;
    Month *previousMonth, *nextMonth;
    Year *year;
    Day *days;

    Month(unsigned int monthNumber, Year *year){
        this -> monthNumber = monthNumber;

        switch(monthNumber){
        case 0:
            name = "January";
            break;
        case 1:
            name = "February";
            break;
        case 2:
            name = "March";
            break;
        case 3:
            name = "April";
            break;
        case 4:
            name = "May";
            break;
        case 5:
            name = "June";
            break;
        case 6:
            name = "July";
            break;
        case 7:
            name = "August";
            break;
        case 8:
            name = "September";
            break;
        case 9:
            name = "October";
            break;
        case 10:
            name = "November";
            break;
        case 11:
            name = "December";
            break;
        }

        previousMonth = NULL;
        nextMonth = NULL;
        this -> year = year;
    }

    void createDays();

};

struct Year{
    unsigned int year;
    Year *previousYear, *nextYear;
    Month *months;

    Year(unsigned int year){
        this -> year = year;
        previousYear = NULL;
        nextYear = NULL;
    }

    void createMonths(){
        Month *currentMonth = months;
        unsigned int monthNumber;

        for(unsigned int i = 0; i < 12; i++){
            months = new Month(i, this);
        }

        while(currentMonth != NULL){
            monthNumber = currentMonth -> monthNumber;

            if(monthNumber == 0 && previousYear != NULL){
                months[monthNumber].previousMonth = &(previousYear -> months[11]);
                months[monthNumber].nextMonth = &(months[monthNumber+1]);
            }
            else if(monthNumber == 11 && nextYear != NULL){
                months[monthNumber].nextMonth = &(nextYear -> months[0]);
                months[monthNumber].previousMonth = &(months[monthNumber-1]);
            }
            else{
                if(monthNumber != 0)
                    months[monthNumber].previousMonth = &(months[monthNumber-1]);
                if(monthNumber != 11)
                    months[monthNumber].nextMonth = &(months[monthNumber+1]);
            }
        }

        currentMonth = months;

        while(currentMonth != NULL){
            currentMonth -> createDays();
            currentMonth = currentMonth -> nextMonth;
        }
    }

    bool isLeapYear(){
        if(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
            return true;
        else
            return false;
    }
};

void Month::createDays(){
    if(name == "January" || name == "March" || name == "May" || name == "July" || name == "August" || name == "October" || name == "December")
        monthLength = 31;
    else if(name != "February")
        monthLength = 30;
    else{
        if(year -> isLeapYear() == true)
            monthLength = 29;
        else
            monthLength = 28;
    }

    days = new Day[monthLength];

    for(unsigned int i = 0; i < monthLength; i++){
        days[i].day = i+1;

        if(i == 0 && previousMonth == NULL)
            days[i].setDayNumber(2);
        else if(i == 0)
            days[i].setDayNumber(previousMonth -> days[previousMonth -> monthLength - 1].dayNumber);
        else
            days[i].setDayNumber(days[i-1].dayNumber);
    }
}
2 голосов
/ 27 марта 2012

Я думаю, вы можете объявить их следующим образом:

// Declare type year, since we need it to create a pointer
// We can finish the type later
struct Year;

struct Month{
    string name;
    unsigned int monthLength;
    struct Month *previousMonth, *nextMonth;
    struct Year *year;
    struct Day *days;
};

struct Year{
    unsigned int year;
    struct Year *previousYear, *nextYear;
    struct Month months[12]; // We need to know how big Month is here
};

Причина, по которой он работает, заключается в том, что компилятору не нужно ничего знать о типах, когда он их использует (поскольку они являются указателями), ему просто нужно знать, что эти типы существуют.

РЕДАКТИРОВАТЬ: Я действительно должен был переместиться на Month до года, поскольку ему нужна эта информация для struct Month months[12];

1 голос
/ 27 марта 2012

Переместить setDayNumber () из объявления структуры: Day :: setDayNumber (...) {}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...