Объявление функции внутри или вне класса


91

Я разработчик JAVA, который пытается изучить C ++, но я действительно не знаю, как лучше всего использовать стандартные объявления функций.

В классе:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

Или снаружи:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

Такое ощущение, что второй может быть менее читабельным ...


1
На самом деле здесь есть 3 варианта. Во втором примере определение функции может быть в файле заголовка (но все еще не встроено) или в отдельном .cppфайле.
Коди Грей

Этот вопрос может помочь вам понять.
Björn Pollex

3
Просто примечание: объявление всегда находится внутри класса, но определение находится либо внутри, либо снаружи. Заголовок и текст вопроса должны быть подвергнуты s / декларация / определение / Не верите мне? stackoverflow.com/q/1410563/1143274
Евгений Сергеев

1
Следует избегать определений функций внутри класса. Они считаются неявными inline.
Джон Строуд,

@JohnStrood так? inlineослабляет только одно правило определения, которое необходимо, если используется другая единица переводаClazz
Caleth

Ответы:


57

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

Однако, в отличие от Java, C ++ не заставляет вас группировать определения функций в классы: стандартный способ объявления функции C ++ - просто объявить функцию без какого-либо класса.

Если вместо этого вы говорите об объявлении / определении метода, то стандартный способ состоит в том, чтобы поместить только объявление во включаемый файл (обычно с именем .hили .hpp) и определение в отдельный файл реализации (обычно с именем .cppили .cxx). Я согласен, что это действительно несколько раздражает и требует некоторого дублирования, но так был разработан язык.

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

Примечание: даже если вы знаете Java, C ++ - это совершенно другой язык ... и это язык, который нельзя изучить путем экспериментов. Причина в том, что это довольно сложный язык с множеством асимметрий и явно нелогичным выбором, и, что наиболее важно, когда вы делаете ошибку, нет «ангелов ошибок времени выполнения», которые спасут вас, как в Java ... но вместо этого есть » демоны неопределенного поведения ".

Единственный разумный способ изучить C ++ - это прочитать ... независимо от того, насколько вы умны, вы не сможете угадать, что решил комитет (на самом деле умение иногда даже является проблемой, потому что правильный ответ нелогичен и является следствием исторического наследие.)

Просто выберите одну или две хорошие книги и прочитайте их от корки до корки.


7
Если кто-то приходит с Java и просит помощи по C ++, то что он ему говорит, если вы говорите «язык, который, как вы знаете, чем-то помешан»? У него нет сравнения с другими языками, так что это ему почти ничего не говорит. Лучше, чем использовать такое сильное эмоционально окрашенное слово, как одержимый, которое мало что говорит об ОП, вы можете просто пропустить эту часть. Более того, каков контекст выражения «использовать класс для всего»? В Java вы не используете класс для метода. Вы не используете класс для переменной. Вы не используете класс для файла .. Так что здесь "все"? Рантинг?
Дэниел С.

3
@DanielS: Убрал эту часть, потому что, очевидно, вас обидел (не знаю почему). Конечно, я не разглагольствую о Java, потому что я вообще не использую Java, я просто думал в то время, что ООП как объектно-ориентированное программирование было забавной шуткой, хотя, очевидно, это не так. Я был сертифицированным программистом Java 1.1, но тогда решил, что я не буду использовать этот «язык программирования», если только его не заставят по какой-то причине, и пока мне удавалось его избегать.
6502

Спасибо, я думаю, теперь он читается намного лучше. Извините, если я обиделась. В следующий раз я постараюсь быть более позитивным.
Daniel S.

15
Не отвечает на вопрос
Петр Пеллер

1
@PetrPeller: какая часть третьего абзаца вам непонятна?
6502

27

Первый определяет вашу функцию-член как встроенную функцию , а второй - нет. Определение функции в этом случае находится в самом заголовке.

Вторая реализация поместит определение функции в файл cpp.

Оба семантически различны, и дело не только в стиле.


2
cplusplus.com/doc/tutorial/classes дает тот же ответ: «Единственная разница между определением функции-члена класса полностью внутри своего класса или включением только прототипа, а затем его определение, заключается в том, что в первом случае функция будет автоматически рассматривается компилятором как встроенная функция-член, а во втором случае это будет обычная (не встроенная) функция-член класса, которая фактически не предполагает никакой разницы в поведении ".
Buttons840

18

Определение функции лучше вне класса. Таким образом, ваш код может оставаться в безопасности, если потребуется. Заголовочный файл должен содержать только объявления.

Предположим, кто-то хочет использовать ваш код, вы можете просто дать ему файл .h и файл .obj (полученный после компиляции) вашего класса. Для использования вашего кода ему не нужен файл .cpp.

Таким образом, ваша реализация никому не будет видна.


10

Метод «Внутри класса» (I) действует так же, как метод «вне класса» (O).

Однако (I) можно использовать, когда класс используется только в одном файле (внутри файла .cpp). (O) используется, когда он находится в файле заголовка. cpp файлы всегда компилируются. Заголовочные файлы компилируются, когда вы используете #include "header.h".

Если вы используете (I) в файле заголовка, функция (Fun1) будет объявляться каждый раз, когда вы включаете #include "header.h". Это может привести к многократному объявлению одной и той же функции. Это сложнее скомпилировать и даже может привести к ошибкам.

Пример правильного использования:

File1: "Clazz.h"

//This file sets up the class with a prototype body. 

class Clazz
{
public:
    void Fun1();//This is a Fun1 Prototype. 
};

File2: "Clazz.cpp"

#include "Clazz.h" 
//this file gives Fun1() (prototyped in the header) a body once.

void Clazz::Fun1()
{
    //Do stuff...
}

File3: "UseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.

File4: "ТакжеUseClazz.cpp"

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header. 

File5: "DoNotUseClazzHeader.cpp"

//here we do not include Clazz.h. So this is another scope. 
class Clazz
{
public:
    void Fun1()
    {
         //Do something else...
    }
};

class MyClazz; //this is a totally different thing. 
MyClazz.Fun1(); //this does something else. 

Вы имеете в виду Clazz MyClazzи Clazz MyClazz2?
Chupo_cro

4

Функции-члены могут быть определены в определении класса или отдельно с помощью оператора разрешения области видимости ::. Определение функции-члена в определении класса объявляет функцию встроенной, даже если вы не используете встроенный спецификатор. Итак, вы можете определить функцию Volume (), как показано ниже:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

     double getVolume(void)
     {
        return length * breadth * height;
     }
};

Если хотите, можете определить ту же функцию вне класса, используя оператор разрешения области видимости :: следующим образом

double Box::getVolume(void)
{
   return length * breadth * height;
}

Здесь важно только то, что вам придется использовать имя класса непосредственно перед оператором ::. Функция-член будет вызываться с использованием оператора точки (.) На объекте, где она будет управлять данными, связанными с этим объектом, только следующим образом:

Box myBox;           

myBox.getVolume();  

(с: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), оба способа разрешены.

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

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


1
Можете ли вы перенести релевантный контент из этой ссылки в тело вашего сообщения и, таким образом, защитить себя от мертвых ссылок? Спасибо
JustinJDavies

2

Первый должен быть помещен в файл заголовка (где находится объявление класса). Второй может быть где угодно, либо в заголовке, либо, как правило, в исходном файле. На практике вы можете поместить небольшие функции в объявление класса (которое объявляет их неявно встроенными, хотя в конечном итоге компилятор решает, будут ли они встроены или нет). Однако у большинства функций есть объявление в заголовке и реализация в файле cpp, как во втором примере. И нет, я не вижу причин, по которым это было бы менее читабельно. Не говоря уже о том, что вы можете разделить реализацию типа на несколько файлов cpp.


1

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

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

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


inline практически не имеет ничего общего с встраиванием. Тот факт, что функции-члены, определенные в строке, неявно объявляются встроенными, необходим, чтобы избежать нарушений ODR.
Big Temp

0

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.