Оголошення функції всередині або поза класом


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
Калет,

Відповіді:


57

С ++ є об'єктно-орієнтованою в тому сенсі, що підтримує об'єктно-орієнтовану парадигму для розробки програмного забезпечення.

Однак, на відміну від Java, C ++ не змушує групувати визначення функцій у класах: стандартний спосіб декларування функції C ++ полягає у простому оголошенні функції без будь-якого класу.

Якщо замість цього ви говорите про декларацію методу / визначення, то стандартним способом є розміщення лише декларації у файлі include (зазвичай з іменем .hабо .hpp), а визначення в окремому файлі реалізації (як правило, з іменем .cppабо .cxx). Я згоден, що це справді дещо дратує і вимагає певного дублювання, але саме так була розроблена мова.

Для швидких експериментів та однофайлових проектів все буде працювати ... але для більших проектів це розділення є те, що практично потрібно.

Примітка: Навіть якщо ви знаєте Java, C ++ - це зовсім інша мова ... і це мова, яку не можна вивчити експериментуючи. Причина полягає в тому, що це досить складна мова з великою кількістю асиметрій та, мабуть, нелогічного вибору, і головне, коли ви помиляєтесь, немає "ангелів помилок виконання", які б врятували вас, як на Java ... але натомість є " невизначена поведінка демонів ".

Єдиний розумний спосіб вивчити C ++ - це читання ... яким би розумним ти не був, ти не можеш здогадатися, що вирішив комітет (насправді бути розумним іноді навіть проблема, оскільки правильна відповідь нелогічна і є наслідком історичного спадщини.)

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


7
Якщо хтось приходить з Java і просить про допомогу на C ++, то що це йому говорить, якщо ви кажете "мова, яку ви знаєте, чимось одержима"? У нього немає порівняння з іншими мовами, тому це майже нічого не говорить йому. Краще, ніж використовувати вкрай емоційно конотоване слово, як одержимий, що не багато говорить ОП, ви можете подумати про те, щоб просто залишити цю частину поза увагою. Більше того, який контекст "використовувати клас для кожного"? У Java ви не використовуєте клас для методу. Ви не використовуєте клас для змінної. Ви не використовуєте клас для файлу .. То що тут "все"? Поїздка?
Даніель С.

3
@DanielS: Вилучив цю частину, бо, мабуть, образив тебе (не знаю, чому). Звичайно, я не скажу про Java, оскільки я насправді взагалі не використовую Java, я просто думав тоді, що ООП як програмоване об'єктами програвання було смішним жартом, хоча, мабуть, це не так. Я був сертифікованим програмістом Java 1.1, але тоді вирішив, що, якщо з якихось причин не буду змушений, я не буду використовувати цю "мову програмування", і поки що мені вдалося цього уникнути.
6502,

Дякую, думаю, зараз це читається набагато краще. Вибачте, якщо я звучу ображено. Спробую наступного разу бути більш позитивним.
Даніель С.

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". Це може призвести до декларування однієї і тієї ж функції кілька разів. Це важче скласти, і навіть може призвести до помилок.

Приклад правильного використання:

Файл1: "Clazz.h"

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

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

Файл 2: "Clazz.cpp"

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

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

Файл 3: "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.

Файл4: "AlsoUseClazz.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. 

Файл5: "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" практично не має нічого спільного з вкладанням. Той факт, що функції-члени, визначені в рядку, неявно оголошуються вбудованими, полягає у тому, щоб уникнути порушень ОРС.
Big Temp

0

Вбудовані функції (функції, коли ви оголошуєте їх у класі) кожного разу, коли їх викликаєте, вони вставляються у ваш основний код пам'яті. Коли ви оголошуєте функцію поза класом, коли ви викликаєте функцію, вона надходить з тієї ж пам'яті. Ось чому це набагато краще.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.