Wprowadzenie do programowanie
Wprowadzenie do programowanie
obiektowego w języku C++
obiektowego w języku C++
Obiekty i klasy
Część druga Roman Simiński [email protected] www.us.edu.pl/~siminski Autor KontaktObiekty i klasy w języku C++
Obiekty i klasy w języku C++
Problem
Przewidywane jest napisanie obiektowej wersji programu, realizującego obliczenia z wykorzystaniem pól różnych, płaskich figur geometrycznych.
Należy zdefiniować klasy opisujące takie figury.
Kwadrat
Koło Prostokąt
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Analiza obiektowa
Stosując zasadę abstrakcji wyodrębniamy najistotniejsze cechy obiektów dla rozpatrywanego zagadnienia — obliczeń pól figur płaskich.
Kwadrat
Koło Prostokąt
Trójkąt
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Analiza obiektowa — klasa opisu kwadratu Square
Kwadrat side Square ? side Modelowany obiekt Model analityczny Model implementacyjny
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Hermetyzacja a pola publiczne i prywatne
Stosując zasadę hermetyzacji ukrywamy dane w części prywatnej i zapewniamy dostęp poprzez metody dostępowe (interfejsowe).
Pola publiczne a pola prywatne
s.side = 100; // Brak hermetyzacji, bezpo redni dost p do pólś ę cout << s.side; // Brak hermetyzacji, bezpo redni dost p do pólś ę
s.setSide( 100 ); // Hermetyzacja, dost p do pola za pomoc modyfikatoraę ą cout << s.getSide(); // Hermetyzacja, dost p do pola za pomoc akcesoraę ą
Funkcje publiczne klasy można nieformalnie podzielić na;
akcesory — funkcje umożliwiające pobieranie wartości pól, akcesorem jest np.
metoda getSide.
modyfikatory — funkcje dokonujące modyfikacji wartości pól, modyfikatorem jest
np. metoda setSide.
realizatory — funkcje realizujące właściwe dla danej klasy usługi, realizatorem jest
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Stosowanie pól publicznych
Square
+setSide( double newSide ) +getSide()
+area()
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Obiekt klasy Square od strony programisty-użytkownika
Square s;
Deklaracja obiektu s klasy Square:
s.setSide( 100 );
Ustalenie boku o długości 100
double p;
p = s.area();
Obliczenie pola kwadratu:
cout << "Pole kwadratu wynosi: " << s.area();
Obliczenie i wyprowadzenie pola kwadratu do stdout:
cout << "Bok kwadratu: " << s.getSide(); Pobranie aktualnej długości boku:
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Szkic programu obliczającego pole kwadratu
#include <iostream> using namespace std; // ??? int main() { double num; Square s;
cout << endl << "Obliczam pole kwadratu" << endl; cout << "Podaj bok: ";
cin >> num;
s.setSide( num );
cout << "Pole kwadratu wynosi: " << s.area(); return EXIT_SUCCESS;
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Deklaracja klasy Square
class Square {
public :
// Składowe publiczne
void setSide( double newSide ); double getSide(); double area(); private: // Składowe prywatne double side; };
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Sekcje private i public
Dwie podstawowe sekcje:
private — elementy zadeklarowane w tej sekcji mogą być wykorzystywane
wyłącznie przez funkcje składowe danej klasy. Elementami tymi mogą być zarówno pola i funkcje. Mówi się o nich, że są prywatne.
public — elementy zadeklarowane w tej sekcji są dostępne również dla innych
elementów programu. Mówi się o nich, że są publiczne lub stanowią interfejs klasy.
Dwie metody kolejności zapisu sekcji public i private
class C { public: // Cz ć publiczna klasyęś void interfaceMethod(); private: // Cz ć prywatna klasyęś void privateMethod(); int internalData; }; class C { private: // Cz ć prywatna klasyęś void privateMethod(); int internalData; public: // Cz ć publiczna klasyęś void interfaceMethod(); };
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Definicja funkcji składowych
class Square {
public :
void setSide( double newSide ); double getSide();
double area(); private:
double side; };
void Square::setSide( double newSide ) {
side = newSide; }
double Square::getSide() {
return side; }
double Square::area() {
return side * side;
} Funkcje składowe poza deklaracja klasy
W języku C++ występuje operator zakresu ::. Służy np. do deklarowania funkcji składowych poza ciałem
klasy. Jego zastosowanie jest szersze, zapis Square:: oznacza, że
występujący po nim element należy do klasy Square.
Obiekty i klasy w języku C++
Obiekty i klasy w języku C++
Definicja funkcji składowych
class Square {
public :
void setSide( double newSide ) { side = newSide; } double getSide() { return side; } double area() {
return side * side; }
private:
double side;
Konstruktory
Konstruktory
Co się stanie gdy nie ustalimy rozmiaru boku obiektu klasy Square?
Square s;
Square squares[ 3 ];
cout << s.area() << endl;
cout << squares[ 0 ].area() << endl; cout << squares[ 1 ].area() << endl; cout << squares[ 2 ].area() << endl;
? ? ? ?
Jak zainicjować obiekt na etapie jego definiowania?
Konstruktor jest specjalną funkcją, aktywowaną przez kompilator automatycznie
w momencie gdy obiekt jest tworzony. Dzieje się tak zanim programista będzie mógł „dorwać” obiekt. Konstruktor ma przygotować obiekt do „życia”.
Konstruktor to specyficzna funkcja. Konstruktor nie ma typu rezultatu, nosi taką nazwę jak nazwa klasy i zwykle nie wywołuje się go jawnie w kodzie programu.
Konstruktory
Konstruktory
Rodzaje konstruktorów
Występują cztery rodzaje konstruktorów:
Konstruktor domyślny (ang. default constructor) aktywowany, gdy tworzony
jest obiekt bez jawnie określonych danych inicjalizujących.
Konstruktor ogólny (ang. general constructor), zwany też parametrowym,
aktywowany gdy tworzymy obiekt z jawnie określonymi danymi inicjalizującymi.
Konstruktor kopiujący (ang. copy constructor) aktywowany wtedy, gdy
tworzymy obiekt, inicjalizując go danymi z innego obiektu tej samej klasy.
Konstruktor rzutujący (ang. cast constructor) aktywowany wtedy, gdy tworzymy
obiekt, inicjalizując go danymi z obiektu innej klasy. Square s, squares[10]; Square s( 100 ); Square s( 100 ); Square a = s, b( s ); Square s( 100 ); Rectangle c = s, d( s );
Konstruktor domyślny
Konstruktor domyślny ―― default constructor default constructor
Wprowadzamy konstruktor domyślny klasy Square
class Square {
public :
Square(); // Konstruktor domy lny (bezparametrowy)ś
void setSide( double newSide ); double getSide();
double area(); private:
double side; };
Okoliczności aktywowania konstruktora domyślnego
Square a; // Aktywacja: a.Square() Square b; // Aktywacja: b.Square() Square c; // Aktywacja: c.Square()
Square squares[ 3 ]; // Aktywacja: Square() dla ka dego elementu tablicy:ż // squares[ 0 ].Square()
// squares[ 1 ].Square() // squares[ 2 ].Square()
Konstruktor domyślny
Konstruktor domyślny ―― default constructor default constructor
Dwie wersje realizacji konstruktora domyślnego:
Square::Square() : side( 0 ) { } Square::Square() { side = 0; }
Wersja intuicyjnaWersja z listą inicjalizacyjną
Square::Square() : side( 0 )
{
}
Nazwa pola Lista inicjalizująca konstruktoraWyrażenie inicjalizujące Lista inicjalizacyjna ma dwa zastosowania. Pierwsze z nich to
inicjowanie pól obiektu.
Na liście może wystąpić nazwa pola, a w nawiasach wartość temu polu przypisywana.
Drugie zastosowanie listy inicjaliza-cyjnej zostanie omówione później.
Konstruktor parametrowy, inaczej ogólny
Konstruktor parametrowy, inaczej ogólny ―― general constructor general constructor Czy można zainicjować obiekt na etapie deklaracji?
Square s( 100 );
cout << "Pole kwadratu wynosi: " << s.area();
Konstruktor ogólny pozwala na zainicjowanie pól obiektu na etapie jego deklaracji, wartościami określonymi przez programistę.
Square a( 1 ); // Aktywacja: a.Square( 1 ) Square b( 5 ); // Aktywacja: b.Square( 5 )
Konstruktor parametrowy, inaczej ogólny
Konstruktor parametrowy, inaczej ogólny ―― general constructor general constructor Wprowadzamy konstruktor parametrowy (ogólny) klasy Square
class Square {
public : Square();
Square( double startSide );
void setSide( double newSide ); double getSide();
double area(); private:
double side; };
Konstruktor parametrowy, inaczej ogólny
Konstruktor parametrowy, inaczej ogólny ―― general constructor general constructor Dwie wersje realizacji konstruktora:
Square::Square( double startSide ) {
side = startSide; }
Square::Square( double startSide ) : side( startSide ) {
}
Wersja intuicyjna
Konstruktor parametrowy, inaczej ogólny
Konstruktor parametrowy, inaczej ogólny ―― general constructor general constructor Jak zainicjować obiekt const?
const Square cs; // Niezainicjowany obiekt const
cs.setSide( 1.2 ); // Bł d – próba modyfikacji obiektu constą cout << "Pole kwadratu wynosi: " << cs.area();
Słowo kluczowe const oznacza, że wartość zmiennej bądź argumentu nie może być
zmieniana w czasie wykonania programu.
const Square cs( 1.2 ); // Zainicjowany obiekt const cout << "Pole kwadratu wynosi: " << cs.area();
Operator zakresu w akcji
Operator zakresu w akcji
Wykorzystanie operatora zakresu ::
void Square::setSide( double newSide ) {
side = newSide; }
Czy parametr funkcji setSide może nazywać się side? Czyli zamiast:
void Square::setSide( double side ) {
side = side; }
definiujemy funkcję tak:
To nie będzie działać poprawnie
void Square::setSide( double side ) {
Square::side = side; }
Ale można użyć operatora zakresu:
Operator zakresu w akcji
Operator zakresu w akcji
Wykorzystanie operatora zakresu ::,cd. ...
Square::Square( double side ) {
side = side; }
Podobny problem występuje w konstruktorze:
Square::Square( double side ) {
Square::side = side; }
Operator zakresu odsłania przysłonięte pole
Square::Square( double side ) : side( side )
{ }
Problem przesłaniania pól nie występuje, gdy stosujemy listę inicjalizującą
Ten konstruktor nie będzie działał poprawnie
Koncepcja przeciążania funkcji
Koncepcja przeciążania funkcji
Dlaczego dwa konstruktory posiadają tę samą nazwę?
Przeciążać można również nazwy zwykłych funkcji int add( int a, int b )
{
return a + b; }
double add( double a, double b ) {
return a + b; }
cout << endl << "Dodawanie int :" << add( 1, 1 )
cout << endl << "Dodawanie double :" << add( 1.0, 1.0 ); W języku C++ istnieje możliwość przeciążania nazw funkcji Identyfikator Square jest przeciążony i oznacza:
konstruktor domyślny Square(),
Funkcje składowe const
Funkcje składowe const
Uzupełniamy prototypy i definicje funkcji słowem kluczowym const
class Square {
public :
Square();
Square( double startSide );
void setSide( double newSide ); double getSide() const;
double area() const; private:
double side; };
double Square::getSide() const
{
return side; }
double Square::area() const
{
return side * side; }
Metody ze specyfikacją const nie mogą modyfikować pól obiektu, mogą zatem być wywoływane dla obiektów stałych.
Konstruktor kopiujący
Konstruktor kopiujący ―― copy constructor copy constructor Dla typów wbudowanych można tak:
int j = 1; int i = j;
Czy można tak samo dla obiektów?
Square first( 100 ); Square second = first;
Konstruktor kopiujący (ang. copy constructor), odpowiedzialny za skopiowanie
Konstruktor kopiujący
Konstruktor kopiujący ―― copy constructor copy constructor Najlepiej zdefiniować konstruktor kopiujący
class Square {
public : Square();
Square( double side );
Square( Square & otherSquare );
void setSide( double side ); double getSide() const;
double area() const; private:
double side; };
Operator & oznacza referencję,
umieszczony w deklaracji parametru oznacza przekazanie przez zmienną.
Square::Square( Square & otherSquare ) {
side = otherSquare.side; }
Konstruktor kopiujący
Konstruktor kopiujący ―― copy constructor copy constructor Dwie wersje realizacji konstruktora:
Wersja intuicyjna
Wersja z listą inicjalizacyjną
Square::Square( Square & otherSquare ) : side( otherSquare.side ) {
Konstruktor kopiujący
Konstruktor kopiujący ―― copy constructor copy constructor
Okoliczności aktywowania konstruktora kopiującego
Square first( 100 ); // first.Square( 100 ); Square second = first; // second.Square( first ); Square third( first ); // third.Square( first );
cout << "Pole kwadratu pierwszego wynosi: " << first.area(); cout << "Pole kwadratu drugiego wynosi : " << second.area(); cout << "Pole kwadratu trzeciego wynosi : " << third.area();
W tej sytuacji konstruktor kopiujący nie działa!
Konstruktor kopiujący odpowiedzialny za skopiowanie zawartości obiektów tej samej klasy na etapie inicjalizacji.
Square first( 100 ); Square second;
Konstruktor kopiujący
Konstruktor kopiujący ―― copy constructor copy constructor Nie można:
const Square first( 1 );
Square second = first; // Bł d, niejawna referencja do obiektu constą
Rozwiązanie problemu z obiektem const:
Square::Square( const Square & otherSquare ) : side( otherSquare.side ) {
Będzie jeszcze jeden konstruktor...
Będzie jeszcze jeden konstruktor...
Załóżmy, że istnieje klasa opisu prostokąta ― Rectangle
class Rectangle {
public :
// Konstruktory Rectangle();
Rectangle( double width, double height );
Rectangle( const Rectangle & otherRectangle ); // Modyfikatory
void setWidth( double width ); void setHeight( double height ); // Akcesory
double getWidth() const; double getHeight() const; // Realizator
double area() const; private:
double width, height; };
Będzie jeszcze jeden konstruktor...
Będzie jeszcze jeden konstruktor...
Definicja konstruktorów i realizatora
// Konstruktor domy lnyś
Rectangle::Rectangle() : width( 0 ), height( 0 ) {
}
// Konstruktor ogólny
Rectangle::Rectangle( double width, double height ) : width( width ), height( height )
{ }
// Konstruktor kopiuj cyą
Rectangle::Rectangle( const Rectangle & otherRectangle )
: width( otherRectangle.width ), height( otherRectangle.height ) {
}
// Realizator
double Rectangle::area() const {
return width * height; }
Będzie jeszcze jeden konstruktor...
Będzie jeszcze jeden konstruktor...
Definicja modyfikatorów i realizatorów
// Modyfikatory
void Rectangle::setWidth( double width ) {
Rectangle::width = width; }
void Rectangle::setHeight( double height ) {
Rectangle::height = height; }
// Akcesory
double Rectangle::getWidth() const {
return width; }
double Rectangle::getHeight() const {
return height; }
Konstruktor rzutujący
Konstruktor rzutujący ―― cast constructor cast constructor
Mamy klasę Square i Rectangle, czy można tak:
Square s( 100 ); // Definicja zainicjowanego kwadratu s
Rectangle r( s ); // Definicja prostok ta r, zainicjowanego kwadratem są
Następuje tutaj inicjalizacja obiektu pewnej klasy obiektem innej klasy. Skąd kompilator ma wiedzieć, jak „przepisać” dane pomiędzy obiektami różnych klas? Programista może określić metodę przepisania danych z obiektu jednej klasy do obiektu klasy innej, pisząc konstruktor rzutujący.
Konstruktor rzutujący
Konstruktor rzutujący ―― cast constructor cast constructor
Konstruktor rzutujący odpowiedzialny za skopiowanie zawartości obiektów pewnej klasy do obiektu innej klasy na etapie inicjalizacji.
Potrzebny jest konstruktor rzutujący
Rectangle::Rectangle( const Square & square )
: width( square.getSide() ), height( square.getSide() ) {
}
Zdefiniowany przez programistę sposób zainicjowania wysokości i szerokości prostokąta informacjami pochodzącymi z obiektu klasy opisującej kwadrat.
Informacja dodatkowa
Informacja dodatkowa ―― parametry domyślne parametry domyślne Parametry domyślne:
void fun( int i, float f = 0, char c = ’A’ ); . . .
fun( 10 ); // i == 10, f == 0, c == ’A’ fun( 20, 3.15 ); // i == 20, f == 3.15, c == ’A’ fun( 30, 22.1, ’Z’ ); // i == 30, f == 22.1, c == ’Z’
Parametr domyślny to wartość określona na etapie deklaracji funkcji, która
zostanie automatycznie wstawiona do parametru formalnego, jeżeli dana funkcja zostanie wywołana bez odpowiedniego parametru aktualnego.
Parametry domyślne dotyczą funkcji składowych klas jak i funkcji niezwiązanych z klasami.
Parametry domyślne myszą być definiowane od końca listy parametrów.
Prototypy a parametry domyślne
void fun( int i, float f = 0, char c = ’A’ ); . . .
void fun( int i, float f, char c ) {
}
Jeżeli stosujemy prototypy funkcji, wartości parametrów domyślnych określa się właśnie w prototypie, w definicji funkcji już nie występują.
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor domyślny ― default constructor
A();
A( arg1 = wart1, arg2 = wart2, ... );
Konstruktor domyślny:
Jest bezparametrowy, lub posiada wszystkie parametry będące parametrami
domyślnymi.
Jednoczesne wystąpienie obu powyższych form spowoduje błąd kompilacji. Inicjuje obiekty, deklarowane lub (bądź tworzone) bez parametrów.
Dotyczy to również obiektów będących elementami tablicy. A a, b, c; // Aktywacja: a.A(), b.A(), c.A()
A tab[ 10 ]; // Aktywacja: A() dla ka dego z 10-ciu elementów tab:ż // tab[ 0 ].A(), tab[ 1 ].A(), itd... .
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor domyślny syntetyzowany przez kompilator
Jeżeli dla danej klasy nie zdefiniowano żadnego konstruktora, kompilator
syntetyzuje konstruktor domyślny.
Jeżeli dla danej klasy zdefiniowano jakiś konstruktor inny od domyślnego, a ten jest potrzebny, lecz niezdefiniowany, kompilator zgłosi błąd.
Syntetyzowany konstruktor domyślny nie robi niczego mądrego. Nie należy się np. spodziewać po nim inicjalizacji pól wartościami zerowymi.
Programista powinien zdefiniować jawnie sposób inicjalizacji obiektów definiowanych bezparametrowo.
Służy do tego właśnie konstruktor domyślny, jego definiowanie jest dobrą praktyką. Nie należy ufać konstruktorowi domyślnemu syntetyzowanemu przez kompilator.
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor ogólny ― general constructor
A( arg1, arg2, ... ); Konstruktor ogólny:
Jest to podstawowy konstruktor przeznaczony do inicjowania obiektów na etapie ich deklaracji czy też tworzenia.
Argumenty określają zwykle wartości jakie mają być przypisane określonym polom obiektu.
Konstruktorów głównych może być więcej, mogą one zawierać również parametru domyślne.
Szczególnym przypadkiem jest konstruktor posiadający tylko parametry domyślne, staje się on wtedy konstruktorem domyślnym.
A( int a, float b, char * c = NULL ); A obj1( 2, 3.4, "Ala");
A obj2( 1, 0.0 );
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Wiele konstruktorów ogólnych
Rectangle::Rectangle( float width, float height ) : width( width ), height( height )
{ }
Rectangle::Rectangle( float side ) : width( side ), height( side )
{ }
. . .
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor kopiujący ― copy constructor
A( A & obj );
A( A & obj, arg1 = wart1, ... ); A( const A & obj );
A( const A & obj, arg1 = wart1, ... ); Konstruktor kopiujący:
Jest potrzebny jedynie wtedy, gdy przewidziana jest inicjalizacja obiektu danej klasy innym obiektem tejże klasy:
A obj1;
A obj2 = obj1; A obj3( obj2 ); void fun( A obj ); A obj1; fun( obj1 ); A fun( void ) { . . . }
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor kopiujący a bitowe kopiowanie pole po polu
10 20 width r1 height 10 20 width r2 height Rectangle r1( 10, 20 ); Rectangle r2( r1 );
Kompilator potrafi sobie poradzić z takim przypadkiem. Wykona kopiowanie zawartości obiektu obj1 do obiektów obj2 i obj3 pole po polu, wykonując ich
bitową kopię.
W niektórych przypadkach bitowe kopiowanie pole po polu jest wystarczające.
Wtedy programista nie musi definiować konstruktora kopiującego. Tak na prawdę, w klasach Square i Rectangle konstruktor ten nie jest potrzebny.
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor kopiujący a bitowe kopiowanie pole po polu
Samochod volvoS80( "Volvo", "S80" ); Samochod volvoV50( volvoS80 );
volvoV50.zmienModel( "V50" ); marka marka volvoS80 volvoV50 model model Volvo S80 V50 volvoV50.zmienModel( "V50" );
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor kopiujący a bitowe kopiowanie pole po polu
class A { public: A() : i( 0 ) { } private : A( const A & ); int i; }; A a1;
A a2( a1 ); // A::A(const A &) is not accessible Konstruktor kopiujący ― używać, nie używać?
Stosowanie konstruktora kopiującego jest dobrą, programistyczną praktyką. Dzięki jawnie zdefiniowanym konstruktorom programista ma kontrolę nad kopiowaniem wartości, występujących w wielu, czasem zaskakujących sytuacjach.
Można zablokować możliwość inicjowania obiektów, wartością innego obiektu tej samej klasy:
Podsumowanie informacji o konstruktorach
Podsumowanie informacji o konstruktorach
Konstruktor rzutujący ― cast, type conversion constructor
Konstruktor rzutujący:
Posiada jeden parametr będący referencją obiektu innej klasy. Innych argumentów może nie być lub powinny być one argumentami domyślnymi.
Jest stosowany wszędzie tam, gdzie należy zainicjować obiekt pewnej klasy wartością obiektu innej klasy.
Programista może dzięki konstruktorowi rzutującemu określić w jaki sposób
informacje zapisane w obiekcie klasy B mają zostać odwzorowane (przepisane) w obiekcie klasy A.
A( B & obj );
Funkcje inline
Funkcje inline
Klasa Rectangle raz jeszcze
Funkcje zdefiniowane wewnątrz definicji klasy są traktowane niejawnie jako funkcje inline (rozwijane w miejscu wywołania).
Funkcje inline nie są wywoływane w sposób klasyczny — ich kod jest umieszczany
w miejscu wywołania i w rzeczywistości nie są one wywoływane.
Funkcje inline są preferowanym w C++ zamiennikiem makr definiowanych z wykorzystaniem #define.
class Rectangle {
public :
void setWidth( float width ) {
Rectangle::width = width; }
float getWidth() const {
return width; }
. . . };
Funkcje inline
Funkcje inline
Jak zadeklarować funkcje jako inline poza zasięgiem deklaracji klasy?
class Rectangle {
public : . . .
void setWidth( float width ); float getWidth() const;
. . . };
inline void Rectangle::setWidth( float width ) {
Rectangle::width = width; }
inline float Rectangle::getWidth() const {
return width; }
Funkcje inline
Funkcje inline
Uwagi na temat funkcji inline
Jeżeli funkcje inline mają być wykorzystywane modułach umożliwiających
kompilacje rozłączną, definicja funkcji powinna wystąpić w miejscu jej zwyczajowej deklaracji — w odpowiednim pliku nagłówkowym.
Specyfikacja ze słowem kluczowym inline to tylko rekomendacja dla kompilatora — niektórych funkcji nie można w pełni rozwinąć i będą one wywoływane klasycznie (np. rekurencyjne).
W porównaniu z makrami funkcje inline zapewniają kontrolę typów i wychwytywanie błędów na etapie kompilacji.
Funkcje inline
Funkcje inline
Kod wielokrotnie wykorzystujący pewną funkcję inline:
Może działać szybciej — brak narzutu czasowego związanego z organizacją wywołania funkcji i powrotu z podprogramu;
Będzie dłuższy, zawiera bowiem rozwinięcia ciała funkcji w miejscu jej każdorazowego wywołania.
Mechanizm funkcji zadeklarowanych jako inline przeznaczony jest do optymalizacji
małych, prostych i często wykorzystywanych funkcji.
Dobrym zastosowaniem funkcji inline jest implementacja akcesorów i modyfikatorów realizujących dostęp do prywatnych pól klasy.