Проблемы с заголовками различных классов C++ в отдельных файлах

У меня есть 5 файлов, основной файл и два класса с двумя такими заголовочными файлами:

файл main.cpp:

#include "parent.hpp"
#include <iostream>

int main (){
    Parent parentInstance;
    parentInstance.function();
    return 0;
}

файл parent.hpp:

class Parent {
    public:
        void function();
};

файл parent.cpp:

#include "child1.hpp"

 void Parent::function() {
    Child1 Child1Instance;
    Child1Instance.speak();
}

файл child1.hpp:

#include "parent.hpp"
#include <iostream>

class Child1 : public Parent {
    public:
        void speak();
};

файл child1.cpp:

#include "child1.hpp"

void Child1::speak () {
    std::cout << "Hi, I'm child1" << '\n';
    }

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

 void Parent::function() {
    Child1 Child1Instance;
    Child2 Child2Instance;
    Child1Instance.speak();
    Child2Instance.speak();
}

и верни мне:

Hi, I'm child1 
Hi, I'm child2

Я не вижу здесь цели наследования. Помните, что наследование - это отношения «есть», например Child1 действительно Parent? Возможно, вам следует иметь «родительский» класс отдельный только для Child1 и Child2 (тот, который отличается от класса Parent, который вы показываете), в которых объявлена ​​виртуальная абстрактная функция speak?

Some programmer dude 06.04.2018 13:19

Почему нельзя просто включить child2.hpp так же, как это было с child1.hpp? С какой именно проблемой вы столкнулись?

Karsten Koop 06.04.2018 13:21

@ Karsten-Koop, компилятор с ошибками: parent.hpp: 2: 7: error: переопределение «class Parent» parent.hpp: 2: 7: error: предыдущее определение «class Parent»

Ángel 06.04.2018 13:25

@Someprogrammerdude - вот почему мне так не нравится эта аналогия "ребенок-родитель". Для меня не имеет никакого смысла говорить, что ребенок является родителем, когда вы наследуете «дочерний» класс от «родительского» класса, это полностью вводит в заблуждение.

463035818_is_not_a_number 06.04.2018 13:26

Если Parent::function() пытается создать экземпляры нескольких классов, компилятору требуется видимость определения всех этих типов классов. Это достигается включением всех соответствующих заголовков. Кроме того, поскольку у вас будет parent.h, включенный несколькими заголовками, для него необходимо иметь защиту включения.

Peter 06.04.2018 13:27

@ user463035818 и какой-то чувак-программист, спасибо, теперь я понимаю проблему имени. Я просто выбрал эти имена в спешке для этого примера, они не являются именами реального кода.

Ángel 06.04.2018 13:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
95
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Некоторые правила:

  1. Заголовочный файл всегда имеет включить охранников (или #pragma once).
  2. Каждый .cpp включает соответствующий .hpp в качестве своей первой (без комментариев) строки.
  3. Включайте в .cpp только то, что вы явно используете в этом .cpp.

Это должно решить большинство ваших проблем.


Некоторые пояснения:

  1. Без защиты вы можете дважды включить один и тот же файл транзитивно, скажем, вы включаете a.h и b.h, но b.h уже включает a.h ... Это вызывает некоторые сбивающие с толку ошибки переопределения.
  2. Вы должны включить заголовок, чтобы функция определения в .cpp соответствовала декларации в .hpp. Ставить его на первой строчке - это просто хорошая практика.
  3. Включение именно того, что вы используете, позволяет избежать сбивающих с толку ошибок, если вы забыли #include где-то еще.

Очень благодарен за комментарии, все мои знания - самоучка ... теперь проблема решается легко:

main.cpp без изменений.

файл parent.hpp:

#ifndef _PARENT_HPP_
#define _PARENT_HPP_

class Parent {
    public:
        void function();
};

#endif /* _PARENT_HPP_ */

файл parent.cpp:

#include "parent.hpp"
#include "child1.hpp"
#include "child2.hpp"

 void Parent::function() {
    Child1 Child1Instance;
    Child2 Child2Instance;
    Child1Instance.speak();
    Child2Instance.speak();
}

файлы child1.hpp и child2.hpp (измените 1 на 2):

#include "parent.hpp"
#include <iostream>

class Child1 : public Parent {
    public:
        void speak();
};

файл child1.cpp и child2.cpp (измените 1 на 2):

#include "child1.hpp"

void Child1::speak () {
    std::cout << "Hi, I'm child1" << '\n';
    }

И результат:

Hi, I'm child1
Hi, I'm child2

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

Другие вопросы по теме