Объявить определение класса, отделив его от его тела

Следующий код находится в том же заголовочном файле

#include <iostream>
               
class B
{
public:
    void doSth()
    {  
        A::func(1);
    } 
};

class A
{
public:
    static void func(int a)
    {
        std::cout<<"a = "<< a <<std::endl;
    }
};

Если класс A определен после класса B, как указано выше, компилятор сообщит, что func не является членом «A».

Мой вопрос: можно ли как-нибудь решить эту проблему, не меняя порядок определения классов B и A? Например, разместить объявление класса A где-нибудь над классом B? Спасибо.

Предупреждение о том, что нельзя позволять Sth делать что-либо. Часто заканчивается дуэлями на световых мечах, мощными взрывами и сокрушительным разочарованием.

user4581301 08.08.2024 23:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
68
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Переместить определения функций-членов за пределы классов

#include <iostream>

class B {
 public:
  void doSth();
};

class A {
 public:
  static void func(int a) { std::cout << "a = " << a << std::endl; }
  // Optional: static void func(int a);
};

void B::doSth() { A::func(1); }
// // static
// void A::func(int a) { std::cout << "a = " << a << std::endl; }

Странно, это должно работать, но в моем реальном коде он жалуется на переопределение doSth() внизу..... :(

Nick X Tsui 08.08.2024 23:27

Поскольку это заголовочный файл в вопросе ОП, он должен иметь inline для определения B::doSth, поэтому у него слабая связь.

Eljay 08.08.2024 23:34

Если он находится в заголовке, вам нужно использовать защиту заголовка и ключевое слово inline.

3CxEZiVlQ 09.08.2024 00:42
Ответ принят как подходящий

Если вы хотите, чтобы B::doSth() был объявлен и определен полностью внутри объявления класса B, вам необходимо переместить полное объявление A перед B, например:

#include <iostream>
               
class A
{
public:
    static void func(int a)
    {
        std::cout << "a = " << a << std::endl;
    }
};  

class B
{
public:
    void doSth()
    {  
        A::func(1);
    } 
};

В противном случае, если вам не нужно, чтобы B::doSth() полностью находился внутри объявления класса B, вы можете разделить его объявление и определение, а затем переместить определение после объявления класса A, например:

#include <iostream>
               
class B
{
public:
    void doSth();
};

class A
{
public:
    static void func(int a)
    {
        std::cout << "a = " << a << std::endl;
    }
};

inline void B::doSth()
{  
    A::func(1);
} 

Предпочтительно, чтобы определения методов, которых нет в объявлении класса, были реализованы в отдельных файлах .cpp, например:

А.х.

#ifndef A_H
#define A_H

class A
{
public:
    static void func(int a);
};

#endif

A.cpp

#include <iostream>               
#include "A.h"

void A::func(int a)
{
    std::cout << "a = " << a << std::endl;
}

Б.х.

#ifndef B_H
#define B_H

class B
{
public:
    void doSth();
};

#endif

Б.cpp

#include "B.h"
#include "A.h"
               
void B::doSth()
{  
    A::func(1);
} 

Обратите внимание: если вы оставите определение функции в заголовочном файле, но вне определения класса, оно должно быть отмечено inline (т. е. inline void B::doSth() { ... }). Невыполнение этого требования приведет к нарушению ODR, если заголовок включен в несколько единиц перевода.

Miles Budnek 08.08.2024 23:12

Если вы хотите, чтобы B::doSth() был определен в строке, вы должны переместить объявление A перед B: это не так. Требованием является наличие определения функции в единице перевода, inline void doSth(); во втором варианте кода будет работать.

3CxEZiVlQ 08.08.2024 23:15

@aschepler я перефразировал свою формулировку

Remy Lebeau 08.08.2024 23:21

Вы не можете определить класс отдельно от его «тела», но вы можете определить функцию-член класса вне определения класса.

Например:

class A
{
public:
    static void func(int);
};

class B
{
public:
    void doSth()
    {
        A::func(1);
    }
};

void A::func(int a)
{
    /* Implementation of the function... */
}

Вы, конечно, можете определить функцию B::doSth и снаружи, если порядок определений классов A и B изменить нельзя.

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