/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
jdjespers.. 500
kyllekylle 500
Bech_bb 500
scootergr.. 300
gibson 300
molokyle 287
10  strarup 270
C++ linker fejl: multiple definition of `~
Fra : Preben Holm


Dato : 21-09-05 21:14

Her er output:

------------------------------------------------
preben@laptop deviceparser $ make
g++ -c -o src/Attribute.o src/Attribute.cpp
g++ -c -o src/Device.o src/Device.cpp
g++ -c -o src/DeviceParser.o src/DeviceParser.cpp
g++ -c -o src/main.o src/main.cpp
g++ -c -o src/Property.o src/Property.cpp
g++ -g -lglut -lGL -lGLU -lXmu -lX11 -lXi -lm ./src/Attribute.o
../src/Device.o ./src/DeviceParser.o ./src/main.o ./src/
Property.o -o main
../src/main.o(.text+0x0): In function
`Graphics::DeviceParser::DeviceParser[not-in-charge]()':
: multiple definition of
`Graphics::DeviceParser::DeviceParser[not-in-charge]()'
../src/DeviceParser.o(.text+0x0): first defined here
../src/main.o(.text+0x6e): In function
`Graphics::DeviceParser::DeviceParser[in-charge]()':
: multiple definition of `Graphics::DeviceParser::DeviceParser[in-charge]()'
../src/DeviceParser.o(.text+0x6e): first defined here
../src/main.o(.text+0xdc): In function
`Graphics::DeviceParser:DeviceParser [not-in-charge]()':
: multiple definition of `Graphics::DeviceParser:DeviceParser
[not-in-charge]()'
../src/DeviceParser.o(.text+0xdc): first defined here
../src/main.o(.text+0x11a): In function
`Graphics::DeviceParser:DeviceParser [in-charge]()':
: multiple definition of `Graphics::DeviceParser:DeviceParser
[in-charge]()'
../src/DeviceParser.o(.text+0x11a): first defined here
../src/main.o(.text+0x158): In function
`Graphics::DeviceParser:DeviceParser [in-charge deleting]()':
: multiple definition of `Graphics::DeviceParser:DeviceParser
[in-charge deleting]()'
../src/DeviceParser.o(.text+0x158): first defined here
../src/main.o(.text+0x196): In function
`Graphics::DeviceParser::parseDevFile(std::basic_string<char,
std::char_traits<ch
ar>, std::allocator<char> > const&)':
: multiple definition of
`Graphics::DeviceParser::parseDevFile(std::basic_string<char,
std::char_traits<char>, std::allo
cator<char> > const&)'
../src/DeviceParser.o(.text+0x196): first defined here
../src/main.o(.text+0x23c): In function
`Graphics::DeviceParser::requireOpen(std::basic_ifstream<char,
std::char_traits<c
har> >&, std::basic_string<char, std::char_traits<char>,
std::allocator<char> > const&)':
: multiple definition of
`Graphics::DeviceParser::requireOpen(std::basic_ifstream<char,
std::char_traits<char> >&, std::
basic_string<char, std::char_traits<char>, std::allocator<char> > const&)'
../src/DeviceParser.o(.text+0x23c): first defined here
../src/main.o(.text+0x2fa): In function
`Graphics::DeviceParser::loadNextObject(std::basic_ifstream<char,
std::char_trait
s<char> >&)':
: multiple definition of
`Graphics::DeviceParser::loadNextObject(std::basic_ifstream<char,
std::char_traits<char> >&)'
../src/DeviceParser.o(.text+0x2fa): first defined here
collect2: ld returnerede afslutningskoden 1
make: *** [main] Fejl 1
preben@laptop deviceparser $

----------------------------------------------


Og her er header fil for DeviceParser:
----------------------------------------------
#ifndef _DEVICEPARSER_H_
#define _DEVICEPARSER_H_

#include <vector>
#include <fstream>
#include <iostream>
#include <string>

#include "Device.h"

using std::string;
using std::vector;
using std::ifstream;


namespace Graphics {
   class DeviceParser {
      public:
         DeviceParser();
         virtual ~DeviceParser();

void parseDevFile(const std::string& filename);
void requireOpen(std::ifstream& ifs, const string& file);

protected:

private:
void loadNextObject(std::ifstream& ifs);

vector<Device> _devices;
   };

}

#endif //_DEVICEPARSER_H_
----------------------------------------------

Og her er cpp-fil for DeviceParser:
----------------------------------------------
#include "DeviceParser.h"

namespace Graphics {

DeviceParser::DeviceParser() {
}

DeviceParser:DeviceParser() {
}

void DeviceParser::parseDevFile(const std::string& filename) {
// Load header
// TODO

ifstream ifs(filename.c_str());
requireOpen(ifs, filename);


loadNextObject(ifs);
}

void DeviceParser::requireOpen(std::ifstream& ifs, const string& file) {
if (!ifs.is_open())
throw ("Can't open file '" + file + "'.");
}

void DeviceParser::loadNextObject(std::ifstream& ifs) {
Device device;
char param[30];

while(!ifs.getline(param, 30).eof()) {

if (param[0] == '{') {
// Begin read object
char temp[256];
bool attrib = false;

while(!ifs.getline(temp, 256).eof()) {
std::string s(temp);

if (temp[0] == '}')
break;

if (s.find("Attributes"))
attrib = true;
else {
if (attrib != true) {
// Property found
Property* p = new Property(s);
device.addProperty(*p);
} else {
// Attribute found
Attribute* a = new Attribute(s);
device.addAttribute(*a);
}
}
}
}
}
_devices.push_back(device);
}


}
----------------------------------------------



Og hvad i hele hulen er der så galt?
Jeg har include guards i header-filen jo... og jeg har da ikke multiple
implementationer af mine filer - vel?



Please help...


Med venlig hilsen
Preben Holm

 
 
Bertel Brander (21-09-2005)
Kommentar
Fra : Bertel Brander


Dato : 21-09-05 19:56

Preben Holm wrote:
> ----------------------------------------------
>
> Og her er cpp-fil for DeviceParser:
> ----------------------------------------------
> #include "DeviceParser.h"
>
> namespace Graphics {
>
> DeviceParser::DeviceParser() {
> }

Her vil du få en Graphics::DeviceParser::DeviceParser() for hver
cpp fil der inkluderer headeren

> Jeg har include guards i header-filen jo... og jeg har da ikke multiple
> implementationer af mine filer - vel?

Include guards hjælper for at undgå at få det samme kode
inkluderet flere gange i den samme cpp fil, men IKKE
at få den samme headerfil inkluderet flere gange af
forskellige cpp filer.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Mogens Hansen (21-09-2005)
Kommentar
Fra : Mogens Hansen


Dato : 21-09-05 20:09


"Bertel Brander" <bertel@post4.tele.dk> wrote in message
news:4331acbd$0$25928$edfadb0f@dread12.news.tele.dk...
> Preben Holm wrote:
>> ----------------------------------------------
>>
>> Og her er cpp-fil for DeviceParser:
>> ----------------------------------------------
>> #include "DeviceParser.h"
>>
>> namespace Graphics {
>>
>> DeviceParser::DeviceParser() {
>> }
>
> Her vil du få en Graphics::DeviceParser::DeviceParser() for hver
> cpp fil der inkluderer headeren

Hmmm....
Det er vist ikke helt rigtigt.
Preben Holm skriver at ovenstående kode stammer fra cpp-filen.


Venlig hilsen

Mogens Hansen



Bertel Brander (21-09-2005)
Kommentar
Fra : Bertel Brander


Dato : 21-09-05 20:52

Mogens Hansen wrote:
> "Bertel Brander" <bertel@post4.tele.dk> wrote in message
> news:4331acbd$0$25928$edfadb0f@dread12.news.tele.dk...
>
>>Preben Holm wrote:
>>
>>>----------------------------------------------
>>>
>>>Og her er cpp-fil for DeviceParser:
>>>----------------------------------------------
>>>#include "DeviceParser.h"
>>>
>>>namespace Graphics {
>>>
>>> DeviceParser::DeviceParser() {
>>> }
>>
>>Her vil du få en Graphics::DeviceParser::DeviceParser() for hver
>>cpp fil der inkluderer headeren
>
>
> Hmmm....
> Det er vist ikke helt rigtigt.
> Preben Holm skriver at ovenstående kode stammer fra cpp-filen.

Du har ret.

--
Absolutely not the best homepage on the net:
http://home20.inet.tele.dk/midgaard
But it's mine - Bertel

Mogens Hansen (21-09-2005)
Kommentar
Fra : Mogens Hansen


Dato : 21-09-05 20:08


"Preben Holm" <64bitNOSPAMNO@mailme.dk> wrote in message
news:4331a2a2$0$247$14726298@news.sunsite.dk...

[8<8<8<]
> using std::string;
> using std::vector;
> using std::ifstream;

Brug _aldrig_ using i en en header fil i global scope.
Det kortslutter hele pointen i namespace.

[8<8<8<]
> virtual ~DeviceParser();

Hvorfor har du en virtuel når du ikke har andre virtuelle metoder ?

[8<8<8<]
> void parseDevFile(const std::string& filename);
> void requireOpen(std::ifstream& ifs, const string& file);

Du bruger end ikke dine "using" konsekvent.

[8<8<8<]
> void DeviceParser::loadNextObject(std::ifstream& ifs) {
> Device device;

Får du ikke nemt problemer med hvilken device der ejer henholdvis Attribute
og Property ?

> char param[30];
>
> while(!ifs.getline(param, 30).eof()) {

2 ting:
* Læsning kan fejle af andre grunde end "end-of-file"
* Brug læsning uden fast størrelse buffer og magiske tal. Det er nemmere
sikrere

string param;
while(getline(ifs, param)) {


>
> if (param[0] == '{') {
> // Begin read object
> char temp[256];
> bool attrib = false;
>
> while(!ifs.getline(temp, 256).eof()) {
> std::string s(temp);

Samme her

[8<8<8<]
> Property* p = new Property(s);
> device.addProperty(*p);

Måske bliver det simplere hvis "device" selv opretter "Property":
device.addProperty(s);

[8<8<8<]
> Attribute* a = new Attribute(s);
> device.addAttribute(*a);

Samme her

[8<8<8<]
> _devices.push_back(device);

Der bliver oprettet kopi af device - hvem ejer så Property og Attribute


[8<8<8<]
> Og hvad i hele hulen er der så galt?
> Jeg har include guards i header-filen jo... og jeg har da ikke multiple
> implementationer af mine filer - vel?

Du skulle vel ikke have includeret DeviceParser.cpp i din main.cpp ?

Venlig hilsen

Mogens Hansen



Preben Holm (21-09-2005)
Kommentar
Fra : Preben Holm


Dato : 21-09-05 23:13

>>using std::string;
>>using std::vector;
>>using std::ifstream;
>
>
> Brug _aldrig_ using i en en header fil i global scope.
> Det kortslutter hele pointen i namespace.

Okay... Men kan du forklare mig pointen i namespaces? Eller henvise til
en god forklaring.

Er lige startet på at lære C++ i forrige uge, og skulle gerne "mestre"
det inden for 6 uger (altså 3½ uge endnu) - utopi, det tror jeg nok det
er, men ikke desto mindre et faktum.

Bør f.eks. "using std::string" stå indenfor namespace eller udenfor
namespace i .cpp-filer?


>>virtual ~DeviceParser();
>
>
> Hvorfor har du en virtuel når du ikke har andre virtuelle metoder ?

Fordi Eclipse opretter den som standard virtuel... ikke nogen specielle
årsager overhovedet.
Er endnu ikke rigtig nået til at få læst om virtuelle metoder.


>> void parseDevFile(const std::string& filename);
>> void requireOpen(std::ifstream& ifs, const string& file);
>
>
> Du bruger end ikke dine "using" konsekvent.

Nej, først når "lortet" ikke kompilerer korrekt!


>> void DeviceParser::loadNextObject(std::ifstream& ifs) {
>> Device device;
>
>
> Får du ikke nemt problemer med hvilken device der ejer henholdvis Attribute
> og Property ?

Nah, ikke så længe jeg holder tungen lige i munden... men det er dog
rigtigt, at det er smartere at lade Device oprette Property og Attribute.


>> while(!ifs.getline(param, 30).eof()) {
>
>
> 2 ting:
> * Læsning kan fejle af andre grunde end "end-of-file"
> * Brug læsning uden fast størrelse buffer og magiske tal. Det er nemmere
> sikrere
>
> string param;
> while(getline(ifs, param)) {

og hvis du vidste hvor lang tid jeg har ledt efter en funktion som bare
kunne smide linien over i et streng-objekt istedet for et irriterende
char-array, så havde livet været meget lettere for mig.

Men der er jo utallige af ufatteligt ringe eksempler rundt omkring på
nettet hvor der er utroligt mange eksempler på "slam".
Heriblandt der hvor jeg fandt ovenstående ide.

<<<<<<<<
>> _devices.push_back(device);
>
>
> Der bliver oprettet kopi af device - hvem ejer så Property og Attribute

Ehh... undskyld, men den kopi af device som oprettes, vil vel have samme
pointer til Property og Attribute som i det objekt der ligger på
stacken? Så "device" i vector og "device" på stack refererer vel stadig
til samme objekt? Eller har jeg misforstået noget?

(bortset fra at Property og Attribute gemmes i en vector og derfor
faktisk kopieres istedet -> memory leak)


>>Og hvad i hele hulen er der så galt?
>>Jeg har include guards i header-filen jo... og jeg har da ikke multiple
>>implementationer af mine filer - vel?
>
>
> Du skulle vel ikke have includeret DeviceParser.cpp i din main.cpp ?

Jo... Det har jeg... og hvis du vidste hvor mange timer - mange timer
jeg har ledt efter den fejl, så grinede du mere end du allerede gør..
Så hvis jeg lyder lidt irriteret/sarkastisk i mit sprog, så er der en
grund!!!


Mange mange tusind tak for kommentarer og hjælp!...


Med venlig hilsen
Preben Holm

Mogens Hansen (22-09-2005)
Kommentar
Fra : Mogens Hansen


Dato : 22-09-05 08:55


"Preben Holm" <64bitNOSPAMNO@mailme.dk> wrote in message
news:4331be97$0$255$14726298@news.sunsite.dk...

[8<8<8<]
> Okay... Men kan du forklare mig pointen i namespaces?

Pointen er at kunne skelne 2 klasser der hedder det samme (f.eks. string -
altså std::string og my_lib::string).
Forestil dig at nogen skriver
using std::string
i een header fil.
I en anden header fil skriver man
using my_lib::string;

Så kan man ikke længere skelne hvilken type der menes når man skriver
void foo(const string& param);

I særdeleshed kan man ikke overskue hvilken kode man får til at holde op med
at virke hvis man på et tidspunkt tilføjer
using std::string;
til en header fil.

> Eller henvise til en god forklaring.

Det er langt bedre end hvad man hurtigt kan forklare på en nyhedsgruppe.
Se bogen
C++ Coding Standards
Herb Sutter, Andrei Alexandrescu
ISBN 0-321-11358-6
Item 59.
Det er i det hele taget en god bog - det fåes ikke meget bedre.

I samme omgang bør man nævne
Exceptional C++
Herb Sutter
ISBN 0-201-61562-2

More Exceptional C++
Herb Sutter
ISBN 0-201-70434-X

Effective C++:
Scott Meyers
ISBN: 0-321-33487-6
(det er den nye 3. udgave som jeg ikke har læst - men jeg tør godt antage at
den er god)

More Effective C++: 35 New Ways to Improve Your Programs and Designs
Scott Meyers
ISBN: 0-201-63371-X

Effective STL
Scott Meyers
ISBN: 0-201-74962-9

og så naturligvis
The C++ Programming Language, Special Edition
Bjarne Stroustrup
ISBN 0-201-70073-5
>
> Er lige startet på at lære C++ i forrige uge, og skulle gerne "mestre" det
> inden for 6 uger (altså 3½ uge endnu) - utopi, det tror jeg nok det er,
> men ikke desto mindre et faktum.

Det tager lang tid at komme til bunds, men man kan komme rigtigt langt på et
par uger hvis man bruger det rigtige grundlag til at lære fra (Se reference
længere nede).

>
> Bør f.eks. "using std::string" stå indenfor namespace

Man kan skrive det inde i en klasse erklæring eller funktions definition -
men det er simplest bare helt at lade være med at skrive det i en header
fil.

> eller udenfor namespace i .cpp-filer?

Ja, det skader ikke - for der er ikke nogen der inkluderer en cpp fil -:)

[8<8<8<]
> Nej, først når "lortet" ikke kompilerer korrekt!

Det er ikke for at være efter dig - men for at lære.

[8<8<8<]
> og hvis du vidste hvor lang tid jeg har ledt efter en funktion som bare
> kunne smide linien over i et streng-objekt istedet for et irriterende
> char-array, så havde livet været meget lettere for mig.

Ok - så er vi ved en af mine absolut favorit bøger til indtroduktion af C++:
Accelerated C++
Andrew Koenig, Barbara E. Moo
ISBN 0-201-70353-X
som beskriver det på side 91.
Selvom bogen ikke beskriver hele sproget, så indeholder den hvad du har brug
for til at lave et program i stil med hvad du viser.
Bogen er ikke særligt stor og let at læse.
Kode stilen i bogen ligger tæt på hvad jeg bruger længere nede i dette
indlæg.

Har du skrevet Java tidligere (brugen af "new" og navne konventionen tyder
på det) ?

>
> Men der er jo utallige af ufatteligt ringe eksempler rundt omkring på
> nettet hvor der er utroligt mange eksempler på "slam".

Ja - det er rigtigt ærgeligt og et reelt problem.
Derfor opfordrer jeg også altid til at bruge _gode_ bøger (der findes
_mange_ dårlige) i stedet for mere eller mindre tilfældige hjemmesider, og
jeg plejer (som i dette indlæg) at angive en række _gode_ bøger.

[8<8<8<]
>> Der bliver oprettet kopi af device - hvem ejer så Property og Attribute
>
> Ehh... undskyld, men den kopi af device som oprettes, vil vel have samme
> pointer til Property og Attribute som i det objekt der ligger på stacken?
> Så "device" i vector og "device" på stack refererer vel stadig til samme
> objekt?

Ja, hvis din Device ligner noget i retning af:
class Device
{
//...
private:
Propety* property;
// eller
vector<Property*> properties;
};
og du ikke har gjort noget for at implementere en copy constructor og copy
assignment.

Problemet er at du har 2 "Device" objekter som peger på samme Property
objekt - hvem har ansvaret for at nedlægge Property objektet ?
Hvis "Device" destructoren nedlægger den:
Device:Device()
{
delete property;
}
så vil det første "Device" objekt der bliver nedlagt ødelægge livet for det
andet "Device" objekt.
Hvis destructoren ikke nedlægger "property" hvem gør så ?

Det kan sagtens håndteres med f.eks. reference counting eller endnu nemmere
med boost::shared_ptr (se
http://www.boost.org/libs/smart_ptr/shared_ptr.htm), som nogenlunde bliver
en del af den kommende C++ Standard.

class Device
{
// ...
private:
boost::shared_ptr<Property> property;
};
så skal du ikke gøre yderligere.
Det er enkelt og effektivt.

Det er blot endnu nemmere at undgå det til at begynde med, hvis man ikke har
brug for det.
Et eksempel hvor det ikke kan undgås er hvis Property er en abstrakt
basisklasse og "Device" ikke ved om det konkret er f.eks. "StringProperty"
eller "IntProperty" den indeholder.

Det er nemmere hvis Device, Property og Attribute har value semantik (som
f.eks. "int" og "std::string").
Så skriver man blot (ikke oversat kode):

class Device
{
//...
void addProperty(const std::string& PropertyValue);
void addAttribute(const std::string& AttributeValue);
private
vector<Property> properties;
vector<Attribute> attributes;
};

void Device::addProperty(const std::string& PropertyValue)
{
properties.push_back(Property(PropertyValue));
}

Så har du ikke nogen "new" og derfor heller ikke brug for at skrive "delete"
nogen steder.

> Eller har jeg misforstået noget?

Spørgsmålet er mere om du har gjort dig overvejelsen.
Hvis du programmerer i Java eller C# er der ikke noget at overveje - bortset
fra objekt levetid generelt.
Når man programmerer i C++ er der heller ikke noget at overveje, hvis man
bruger en passende kodestil (2 eksempler er givet i dette indlæg).

>
> (bortset fra at Property og Attribute gemmes i en vector og derfor faktisk
> kopieres istedet -> memory leak)

Memory leak er ikke tilladt

[8<8<8<]
>> Du skulle vel ikke have includeret DeviceParser.cpp i din main.cpp ?
>
> Jo... Det har jeg... og hvis du vidste hvor mange timer - mange timer jeg
> har ledt efter den fejl, så grinede du mere end du allerede gør..

Jeg griner ikke.
Jeg tør væde på at jeg har dummet mig på flere måder end du kan forestille
dig i de mere end 15 år jeg har programmeret C++
Hvordan skulle jeg ellers være i stand til at svare ?

Venlig hilsen

Mogens Hansen



Preben Holm (22-09-2005)
Kommentar
Fra : Preben Holm


Dato : 22-09-05 21:03

Tak for et endnu godt svar...

Søg
Reklame
Statistik
Spørgsmål : 177558
Tips : 31968
Nyheder : 719565
Indlæg : 6408926
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste