Deklaracja a definicja.

2

Mamy klasę w pliku test.cpp:

#ifndef _TEST_H_
#define _TEST_H_
class test
{
private: int t;
public: int value()
{
return t;
}
test(int);
};
#endif

W pliku test.cpp:

#include "test.h"
test::test(int a)
{
t=a;
}

Plik pik.h:

#ifndef _PIK_H_
#define _PIK_H_
int pik();
#endif

pik.cpp:

#include <iostream>
#include <test.h>
using namespace std;
int pik()
{
test t(2);
cout<<"pik: "<<t.value()<<endl;
}

main.cpp:

#include <iostream>
#include "test.h"
#include "pik.h"

int main()
{
test t;
cout<<t.value()<<endl;
pik();
}

oraz plik Makefile:

prog: main.o pik.o test.o
    gcc -o $@ $^
main.o: main.cpp
    gcc -o $@ -c $<
pik.o: pik.cpp
    gcc -o $@ -c $<
test.o: test.cpp
    gcc -o $@ -c $<

I teraz wykażę jakim błędem jest definiowanie metod podczas deklaracji w plikach nagłówkowych.

  1. Metoda value klasy test wystąpi w plikach obiektowych main.o i pik.o oraz test.o. Jedna metoda jest kompilowana 3 razy. Wydłuża to czas kompilacji.
  2. Plik Makefile jest źle skonstruowany, aby pokazać, że zmiana metody w pliku test.h i ponowna kompilacja prowadzi do błędu. Nie ma zależności do plików nagłówkowych. Nowa metoda będzie tylko w pliku test.o. Linker skorzysta z metody z pliku main.o, dlatego, że jest pierwsza na liście. Będzie to stara metoda.

W wielu projektach widziałem takie krtótkie metody. Teraz pokazałem, że pliki nagłówkowe służą tylko do deklaracji. Definiowanie metod i funkcji w plikach nagłówkowych jest błędem. Zapraszam do polemiki.

1
  1. kod który tu wrzuciłeś jest pełen błędów - nie skompiluje się
  2. odpalenie tego makefile, który tu dałeś też się nie powiedzie (nie ten kompilator)

Definiowanie metod i funkcji nie jest błędem, błędem jest nieumiejętność kompilacji. Newbie rzeczywiście może mieć z tym problemy, ale ktoś doświadczony, mający codziennie styczność z tymi zagadnieniami nie będzie miał problemów

0

Definiowanie metod i funkcji w plikach nagłówkowych jest błędem. Zapraszam do polemiki.

Z tego co czytałem w symfonii to nie jest błąd. Będą to metody i funkcje inline. Czyli zostaną dopisane tam gdzie są wywołane.

0

Nie czytałem Symfonii. Ale patrząc na to co utworzył kompilator:
test::value()

08048746 <_ZN4test5valueEv>:
 8048746:	55                   	push   %ebp
 8048747:	89 e5                	mov    %esp,%ebp
 8048749:	8b 45 08             	mov    0x8(%ebp),%eax
 804874c:	8b 00                	mov    (%eax),%eax
 804874e:	83 c0 01             	add    $0x1,%eax
 8048751:	5d                   	pop    %ebp
 8048752:	c3                   	ret

pik()

08048770 <_Z3pikv>:
 8048770:	55                   	push   %ebp
 8048771:	89 e5                	mov    %esp,%ebp
 8048773:	53                   	push   %ebx
 8048774:	83 ec 24             	sub    $0x24,%esp
 8048777:	8d 45 f4             	lea    -0xc(%ebp),%eax
 804877a:	89 04 24             	mov    %eax,(%esp)
 804877d:	e8 d2 ff ff ff       	call   8048754 <_ZN4testC1Ev>
 8048782:	8d 45 f4             	lea    -0xc(%ebp),%eax
 8048785:	89 04 24             	mov    %eax,(%esp)
 8048788:	e8 b9 ff ff ff       	call   8048746 <_ZN4test5valueEv>
...

Funkcje nie są inline.
Ale nie do końca. Funkcje będą inline wtedy gdy zostaną jawnie zadeklarowane inline. Zastanawia mnie jak kompilator radzi sobie z funkcjami inline kompilując jeden plik źródłowy, a funkcja będzie tylko zadelkarowana w pliku nagłówkowym, a zdefiniowana w innym pliku. Może ta odpowiedzialność jest przerzucana na linker. Muszę to sprawdzić.

0

Funkcje będą inline tylko jak kompilator uzna to za stosowne. Słowo kluczowe ma w nosie (albo "bierze je pod uwagę" :P). Tak samo jak definicje metod w plikach nagłówkowych.

0

Co ty opowiadasz ze je ma w nosie ? Przytoczę to co jest napisane w symfonii (mniej więcej). Słowo inline tak jak i register oznacza sugestie dla komplikatora. Jeżeli będzie taka możliwość to to wykona. W dodatku Pan Jerzy wspomniał coś o pętli for, czyli jeżeli inline funkcja ja zawiera nie koniecznie będzie inline.

0

Bumcykowy, TY potrafisz czytac?

0
Bumcykowy napisał(a)

Co ty opowiadasz ze je ma w nosie ? Przytoczę to co jest napisane w symfonii (mniej więcej). Słowo inline tak jak i register oznacza sugestie dla komplikatora. Jeżeli będzie taka możliwość to to wykona. W dodatku Pan Jerzy wspomniał coś o pętli for, czyli jeżeli inline funkcja ja zawiera nie koniecznie będzie inline.

Masz zupełną rację:

**Język C++: Pierwsze starcie. Zbigniew koza. str. 76: **

Kompilator traktuje słówko inline jako wskazówkę by w miarę możliwości daną funkcję implementować jako otwartą

Czyli kompilator nie ma w nosie tej wskazówki. Gdy można jakąś funkcje zadeklarować jako otwartą, kopilator zrobi to automatycznie. Gdy widzi słówko inline dla funkcji której poprzednio nie brał pod uwagę stara się w miarę możliwości zamienić ją na funkcję otwartą. izi

0

Na płocie było napisane "dupa" a jak przejechałem ręką to sobie wbiłem drzazgę! :P
Nie wierzcie we wszystko co napisali w książkach, tym bardziej jeśli zrobili to 5,10 czy 15 lat temu...
Ale możecie dalej wierzyć w to że słówko jak "register" w jakikolwiek sposób wpłynie na generację kodu wynikowego. Proponuje skompilować sobie program do poziomu asemblera (większość kompilatorów pozwala na przerwanie kompilacji po każdym etapie/ generowanie celów pośrednich) i sprawdźcie czy faktycznie są jakieś różnice ;)

0

Wyrażę tutaj swoją opinię. Co do definicji funkcji w pliku h/hpp sprawa wygląda tak, że trzeba uważnie to wykorzystać. Otóż kod taki:

class Przyklad
{
public:
void setI( int _i ) { i = _i; }
int getI(){ return i; }

int skomplikowanyAlgorytm( int a, int b, int c );

private:
int i;
};

W takim wypadku jest sens by setI() i getI() zdefiniować w pliku h/hpp, ponieważ zmienność tych metod jest niemał nieprawdopodobna, dodatkowo jest spora szansa na to, że będziemy mieli inline.

Metoda skomplikowanyAlgorytm natomiast musi być zadeklarowana w cpp, ponieważ jest duże prawdopodobieństwo grzebania w niej. Co to nam da? Ano to że ograniczymy zasięg ponownej kompilacji w przypadku zmiany w algorytmie.

Moim zdaniem definicja w pliku h/hpp nie jest czymś zabronionym, natomiast trzeba wiedzieć czemu się definiuje w h/hpp. Ponadto gdy robimy template to wtedy mamy tylko plik h/hpp gdzie mamy deklarację i definicję, ponieważ szablonów nie da się rozdzielić na h/hpp i cpp.

0
Shalom napisał(a)

Na płocie było napisane "dupa" a jak przejechałem ręką to sobie wbiłem drzazgę! :P
Nie wierzcie we wszystko co napisali w książkach, tym bardziej jeśli zrobili to 5,10 czy 15 lat temu...
Ale możecie dalej wierzyć w to że słówko jak "register" w jakikolwiek sposób wpłynie na generację kodu wynikowego. Proponuje skompilować sobie program do poziomu asemblera (większość kompilatorów pozwala na przerwanie kompilacji po każdym etapie/ generowanie celów pośrednich) i sprawdźcie czy faktycznie są jakieś różnice ;)

Akurat symfonie mam z 2009 roku (już w standardzie iso, nie jak symfonia z przed 10 lat) a sam autor ma pojecie o tym jak mało kto.

Mianowicie zaleca aby funkcje inline nie były dłuższe niż dwie linijki kodu. Bo istnieje duża szansa ze mniej stracimy na przepisaniu funkcji tam gdzie była wywołana niż na procesie jej wywołania (nie inline).

Słówko register, jeżeli kompilator potrafi sobie z taka sugestia poradzić to to zrobi.

0

Endrju
Pisząc sugestia mam na myśli "prośba".

Shalom
Czyli najwyraźniej nic w kwestii inline i register się nie zmieniło.

0
bumcykowy napisał(a)

Akurat symfonie mam z 2009 roku (już w standardzie iso, nie jak symfonia z przed 10 lat) a sam autor ma pojecie o tym jak mało kto.

Mianowicie zaleca aby funkcje inline nie były dłuższe niż dwie linijki kodu. Bo istnieje duża szansa ze mniej stracimy na przepisaniu funkcji tam gdzie była wywołana niż na procesie jej wywołania (nie inline).

Słówko register, jeżeli kompilator [ [nawiasem mówiąc - kompilator, od kompilacji. Od komplikacji jest programista] ] potrafi sobie z taka sugestia poradzić to to zrobi.

Naprawdę myślisz że kompilatory zwracają jakąś uwagę na inline?

Jeśli masz wątpliwości - proszę:

tst1.cpp

#include <stdio.h>

void foo()
{
    printf("%s", "foo!");
}

int main()
{
    foo();
    return 0;
}

tst2.cpp

#include <stdio.h>

inline void foo()
{
    printf("%s", "foo!");
}

int main()
{
    foo();
    return 0;
}
D:\unbeliver>gcc tst1.cpp -o tst1.asm -S -O3

D:\unbeliver>gcc tst2.cpp -o tst2.asm -S -O3

tst1.asm

.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	$16, %eax
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	call	__alloca
	call	___main
	movl	$LC1, (%esp)
	movl	$LC0, %edx
	movl	%edx, 4(%esp)
	call	_printf
	leave
	xorl	%eax, %eax
	ret

tst2.asm

.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	$16, %eax
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	call	__alloca
	call	___main
	movl	$LC1, (%esp)
	movl	$LC0, %eax
	movl	%eax, 4(%esp)
	call	_printf
	leave
	xorl	%eax, %eax
	ret

===================================

D:\unbeliver>gcc tst2.cpp -o tst2.asm -S -O0

D:\unbeliver>gcc tst1.cpp -o tst1.asm -S -O0

tst1.asm

.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	movl	$0, %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -4(%ebp)
	movl	-4(%ebp), %eax
	call	__alloca
	call	___main
	call	__Z3foov
	movl	$0, %eax
	leave
	ret

tst2.asm

_main:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$8, %esp
	andl	$-16, %esp
	movl	$0, %eax
	addl	$15, %eax
	addl	$15, %eax
	shrl	$4, %eax
	sall	$4, %eax
	movl	%eax, -4(%ebp)
	movl	-4(%ebp), %eax
	call	__alloca
	call	___main
	call	__Z3foov
	movl	$0, %eax
	leave
	ret

W obydwóch przypadkach inline wzorcowo zignorowane...
Inline może najwyżej przesądzać w przypadku brzegowym kiedy kompilator nie jest pewien czy jest za długa żeby inline-ować czy nie (ale to taka moja fantazja, równie dobrze może olewać konsekwentnie).

Jeśli chodzi o register - kompilatory przejmują sie nim nie bardziej niż auto - równie dobrze możesz sobie dodać do programu

#define register

i zaręczam że nigdzie w żadnym przypadku różnicy nie będzie.

0

MSM to jest jeden bajer w takim razie. Moza sobie wszystko w pliku .h zrobić. Deklarując je jako inline a one i tak będą zwykle.

1 użytkowników online, w tym zalogowanych: 0, gości: 1