[c++] Haakjes gebruik bij "new" language construct

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
Ik heb een vraag over de syntax voor de allocatie van een object. C++ zelf doet nogal wat zaken onder water voor de aanroep van constructors.
Voornamelijk is mijn vraag: waarom gebruiken sommige mensen haakjes als ze een object op de heap willen maken die een constructor zonder parameters heeft? Voorbeeld:

C++:
1
2
Object* object = new Object(); // Waarom hier de haakjes toevoegen? 
Object* object2 = new Object; // Werkt ook

Mijn grootste probleem hiermee is dat het verwarring kan opleveren als men hetzelfde geintje gebruikt bij allocatie op de stack.


C++:
1
2
Object object(); // Dit is een inline functie en de constructor wordt niet aangeroepen!! 
Object object2; // Default constructor wordt aangeroepen.

Om bovenstaande fout te voorkomen zou mijn mening zijn gebruik voor een object dat alleen een default constructor bevat in geen van beide gevallen haakjes.

Ik heb een simpel testprogramma gemaakt waarbij de constructor/ copy constructor/ asignement operator van Examples een debug melding geven.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
       Examples e;      // Default constructor (0 parameters)
       Examples b = 1;  // Implicit constructor (Single parameter)
       Examples c(1);   // Constructor (Single parameter)
       Examples d(b);   // Copy constructor
       Examples f = c;  // Implicit copy constructor
       Examples x;
       x = b;           // Object is already constructed so this is a assignement operator
       Examples y(); // INLINE FUNCTION! NO CONSTRUCTION
       Examples* examples1Ptr(new Examples);
       Examples* examples2Ptr(new Examples());
       Examples* examples3Ptr = new Examples;
       Examples* examples4Ptr = new Examples();

Output:
code:
1
2
3
4
5
6
7
8
9
10
11
Default constructor 
1 parameter constructor 
1 parameter constructor 
Copy constructor 
Copy constructor 
Default constructor 
Asignment Operator 
Default constructor 
Default constructor 
Default constructor 
Default constructor

Acties:
  • 0 Henk 'm!

Verwijderd

Gehakt schreef op woensdag 02 maart 2011 @ 21:29:
Voornamelijk is mijn vraag: waarom gebruiken sommige mensen haakjes als ze een object op de heap willen maken die een constructor zonder parameters heeft? Voorbeeld:
Voornamelijk omdat ze het verkeerd aangeleerd hebben. En voor consistentie denk ik...ik doe het ook.

Acties:
  • 0 Henk 'm!

  • LauPro
  • Registratie: Augustus 2001
  • Laatst online: 19-09 16:51

LauPro

Prof Mierenneuke®

Ik snap je punt niet helemaal, als je een object als inline functie aanroept (waar ik mij al van af vraag of dat mogelijk is) dan kom je snel genoeg achter dat het niet werkt zoals je het op het oog hebt.

Inkoopacties - HENK terug! - Megabit
It is a war here, so be a general!


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
LauPro schreef op woensdag 02 maart 2011 @ 22:43:
Ik snap je punt niet helemaal, als je een object als inline functie aanroept (waar ik mij al van af vraag of dat mogelijk is) dan kom je snel genoeg achter dat het niet werkt zoals je het op het oog hebt.
Ik vermoed dat bij mij de verwarring is ontstaan doordat de oude Compaq CXX (VMS) compiler hier niet helemaal goed mee omgaat (als ik een goed geheugen heb). Vandaar dat ik het ook als een gevaar zie . Daarnaast ken ik sowieso genoeg mensen die mij niet zouden kunnen vertellen dat je met de blokhaken een inline functie definieert. Ik zal eens kijken of ik binnenkort een testje kan doen met de Compaq compiler

Ik heb even geprobeerd om de klasse een memberfunctie te geven en aan te roepen via de inline functie maar de GCC compiler geeft hier inderdaad gewoon een foutmelding over.

Acties:
  • 0 Henk 'm!

  • jmzeeman
  • Registratie: April 2007
  • Laatst online: 12-09 16:17
Zoals het er nu staat is je inline functie volgens mij een forward declaration. Inline function refereerd in c++ meestal naar een functie met het inline keyword ervoor. Nested functies bestaan volgens mij niet in c++ (lambda functions niet meegerekend).
Ik denk vooral dat het gebruik van haakjes te maken heeft met andere veel gebruikte talen waar de haakjes bij new verplicht zijn. Ik vind het geen punt (en ik zie vrijwel nooit code waarbij het niet gebeurt) aangezien het gebruik van haakjes bij stack variablen zonder parameters gewoon niet mag. Ook zal je bij normaal gebruik,waarbij je je stack variabele ook daadwerkelijk gebruikt, vrijwel altijd gewoon een compiler error krijgen (no overload found, no operator found, cannot cast, of iets dergelijks). Ok, het zijn niet de meest duideljke meldingen voor een beginnend c++ programmeur maar als je hier een halve dag naar zoekt onthoud je wel meteen dat het niet mag :) .
Alleen als je er verder niets mee doet of als je toevallig een cast of overload van de desbetreffende functiepointer signature bestaat en er is een gelijknamige functie gedefineerd kan er een vage situatie ontstaan.
Vage situatie:
Bestand 1
C++:
1
2
3
4
int f(Examples ex)
{return ex.value;}
int f(Examples(*acallback)())
{return (*acallback)().value;}

Bestand 2
C++:
1
2
3
4
5
6
7
8
#include "bestand1.h"
int main()
{
    Examples x();
    int i = f(x);
    //Doe iets met i
    //...
}

ander bestand
C++:
1
2
3
4
5
6
Examples x()
{
    Examples r;
    r.DoSomethingEvil();
    return r;
}

Je moet dus best je best doen zeker als je fatsoenlijke namen aan je functies en variabelen geeft.

[ Voor 24% gewijzigd door jmzeeman op 03-03-2011 00:35 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Gehakt schreef op woensdag 02 maart 2011 @ 21:29:
Voornamelijk is mijn vraag: waarom gebruiken sommige mensen haakjes als ze een object op de heap willen maken die een constructor zonder parameters heeft?
Om 'm te default-initializen. Als een object een gespecificeerde ctor heeft dan is er geen verschil, maar voor POD types en aggregates weldegelijk. 'new int' zal een pointer naar ongeinitalizeerde int teruggeven, 'new int()' zal de int op 0 initialiseren.
C++:
1
Object object(); // Dit is een inline functie en de constructor wordt niet aangeroepen!! 
Nee, dat is een functiedeclaratie. Een inline functie is een functie waar de inline declarator voor staat.
C++:
1
Examples b = 1;   // Constructor (Single parameter)
Let op, feitelijk staat daar:
C++:
1
Examples b(Examples(1));

Oftewel, je copy construct een Example(1). Dit mag een compiler optimizen (dus doen alsof er 'Example b(1)' staat), maar de copy ctor moet wel accessible zijn.

[ Voor 18% gewijzigd door .oisyn op 03-03-2011 02:00 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
Het lijkt me sowieso geen forward declaration. Want als je het adres van y meegeeft aan een functie die een pointer van het type Examples verwacht zegt de compiler dat deze het adres van een functiepointer heeft ontvangen.
C++:
1
2
3
4
5
6
7
8
9
void test(Examples* example)
{
}

int main(void)
{
       Examples y();
       test(&y);
}

Error:

code:
1
error: cannot convert "Examples (*)()" to "Examples*" for argument "1" to "void test(Examples*)"
.oisyn schreef op donderdag 03 maart 2011 @ 01:18:
[...]
Nee, dat is een functiedeclaratie. Een inline functie is een functie waar de inline declarator voor staat.
Je was me net voor :P
.oisyn schreef op donderdag 03 maart 2011 @ 01:18:
Let op, feitelijk staat daar:
C++:
1
Examples b(Examples(1));

Oftewel, je copy construct een Example(1). Dit mag een compiler optimizen (dus doen alsof er 'Example b(1)' staat), maar de copy ctor moet wel accessible zijn.
Dit snap ik even niet want zoals je kan zien in de output wordt op dat punt niet de copy constructor aangeroepen?

Voor constructors met 1 paramater zal de compiler deze aanroepen.
code:
1
2
Examples x = 1; // wordt door de compiler gezien als
Examples x(1);

[ Voor 80% gewijzigd door Gehakt op 03-03-2011 01:27 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Gehakt schreef op donderdag 03 maart 2011 @ 01:20:
Het lijkt me sowieso geen forward declaration.
Het is dus wel een declaratie (de "forward" is een beetje onzinnig, backward declarations heb je niet zoveel aan). Een declaratie van een functie, maar wellicht dat jij een forward declaration als iets anders interpreteert?
Je was me net voor :P
Que? Jij bent degene die het inline functies noemt :)
Gehakt schreef op donderdag 03 maart 2011 @ 01:20:
Dit snap ik even niet want zoals je kan zien in de output wordt op dat punt niet de copy constructor aangeroepen?
Zoals ik al zei, de compiler mag het wegoptimizen. Jouw compiler heeft dat dus gedaan. Declareer de copy ctor maar eens als private, dan krijg je een error op die regel.

[ Voor 29% gewijzigd door .oisyn op 03-03-2011 01:27 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
Ja in mijn opvatting staan de forward declarations van functies ofwel klassen altijd in een header om aan de compiler kenbaar te maken dat er een dergelijke functie of klasse (type) later gedefineerd wordt.

Maar hoe moet ik het dan precies zien? Want het declareren van een functie lijkt me in de body van een andere functie beetje onzinnig op die manier want je mag hem daar niet implementeren.
.oisyn schreef op donderdag 03 maart 2011 @ 01:26:
[...]

Zoals ik al zei, de compiler mag het wegoptimizen. Jouw compiler heeft dat dus gedaan. Declareer de copy ctor maar eens als private, dan krijg je een error op die regel.
Mijn compiler (GCC) geeft daar geen error op hoor.
Output:
code:
1
2
3
4
5
Default constructor 
1 parameter constructor 
1 parameter constructor 
Copy constructor 
Copy constructor

Vreemd genoeg zeurde hij wel over de copy constructor bij de volgende regels:
code:
1
2
 Examples x;
 x = b;           // Object is already constructed so this is a assignement operator

Terwijl op dat punt duidelijk de toekennings operator gebruikt wordt.
code:
1
2
../ConstructorExamples/Examples.h:12: error: ‘Examples& Examples::operator=(const Examples&)’ is private
../ConstructorExamples/main.cpp:29: error: within this context

[ Voor 55% gewijzigd door Gehakt op 03-03-2011 01:36 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

De declaratie declareert de functie in de enclosing namespace van de huidige scope, maar de declaratie zelf is alleen zichtbaar binnen die scope.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
    void foo(); // declaratie
    foo(); // ok, foo() is gedeclareerd
}

void bar()
{
    foo(); // error, geen declaratie van foo() zichtbaar
}

void foo()
{
}
Gehakt schreef op donderdag 03 maart 2011 @ 01:30:
Mijn compiler (GCC) geeft daar geen error op hoor.
Dan doe je iets verkeerd.

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Foo
{
public:
    Foo(int) { }

private:
    Foo(const Foo&);
};

int main()
{
    Foo f = 2;
}


test.cpp: In function 'int main()':
test.cpp:7: error: 'Foo::Foo(const Foo&)' is private
test.cpp:12: error: within this context

[ Voor 58% gewijzigd door .oisyn op 03-03-2011 01:41 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
.oisyn schreef op donderdag 03 maart 2011 @ 01:34:
De declaratie declareert de functie in de enclosing namespace van de huidige scope, maar de declaratie zelf is alleen zichtbaar binnen die scope.

Dit is dus geldige code:
C++:
1
...
Ah nu snap ik hem. Zo had ik er nog niet over nagedacht maar het dit maakt het helder.

Het is al laat. Je had gelijk ik had inderdaad de toekenningsoperator private gemaakt. d0h. Maar dan komt er een hele andere vraag bij mij op.

Waarom gaat de compiler daar plat op terwijl duidelijk is dat de code uit de copy constructor niet wordt uitgevoerd maar alleen de code uit de constructor die 1 parameter heeft. Dat lijkt me nogal vreemd.

De implementaties voor het gemak:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Examples::Examples()
{
   qDebug() << "Default constructor";
}

Examples::Examples(int a)
{
   qDebug() << "1 parameter constructor";
}

Examples::Examples(const Examples& examples)
{
   qDebug() << "Copy constructor";
}

Examples& Examples::operator=(const Examples& examples)
{
   qDebug() << "Asignment Operator";
}

[ Voor 50% gewijzigd door Gehakt op 03-03-2011 01:47 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ook leuk:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

struct Foo
{
    Foo(int) { std::cout << "Foo(int)" << std::endl; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)" << std::endl; }
};

int main()
{
    Foo f(Foo(2));
}

Output:
Foo(int)

Met zowel gcc als VC++. Schrijf je code dus nooit zo dat hij afhankelijk is van side-effects in de copy ctor, aangezien die weggeoptimaliseerd kunnen worden.
Gehakt schreef op donderdag 03 maart 2011 @ 01:41:
Maar dan komt er een hele andere vraag bij mij op.

Waarom gaat de compiler daar plat op terwijl duidelijk is dat de code uit de copy constructor niet wordt uitgevoerd maar alleen de code uit de constructor die 1 parameter heeft. Dat lijkt me nogal vreemd.
Daar is niets vreemds aan. De code moet niet ill-formed zijn. Technisch staat er Example b(Example(2)), dus moet er ook op die manier op rechten e.d. gecheckt worden. Dat een call weggeoptimaliseerd kan worden doet daar verder niets aan af. Let wel, de compiler mág het optimizen, maar het hoeft dus niet. Dan zou het dus aan de optimalisatie-instellingen liggen of de code correct is of niet, wat natuurlijk nogal suf is.

Overigens lijkt het antwoord op je originele vraag nu een beetje ondergesneeuwd, is die je wel opgevallen?
.oisyn schreef op donderdag 03 maart 2011 @ 01:18:
[...]

Om 'm te default-initializen. Als een object een gespecificeerde ctor heeft dan is er geen verschil, maar voor POD types en aggregates weldegelijk. 'new int' zal een pointer naar ongeinitalizeerde int teruggeven, 'new int()' zal de int op 0 initialiseren.

[ Voor 79% gewijzigd door .oisyn op 03-03-2011 02:00 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
.oisyn schreef op donderdag 03 maart 2011 @ 01:46:
Overigens lijkt het antwoord op je originele vraag nu een beetje ondergesneeuwd, is die je wel opgevallen?
Ja die had ik gezien. Ik wil het graag nog even uitproberen. Wat bedoel je met een "non trivial" ctor? Bedoel je daarmee hetzelfde als wat ik een default ctor noem? (ctor zonder parameters) Ik moet morgen even een voorbeeldje bouwen om het verschil te zien zoals jij het beweert. :) Maar in principe maakt het dus alleen uit voor POD en types die samengesteldzijn uit POD. Volgens mij mogen dat soort nieuwe type ook het label POD dragen als je geen ctor/functies implementeert.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Wat bedoel je met een "non trivial" ctor?
Dat klopte eigenlijk niet, ik had het al aangepast voordat jij je post plaatste ;)

Een non trivial ctor is een ctor die expliciet gespecificeerd is, of gegenereerd door de compiler (bijvoorbeeld omdat een member over een non-trivial ctor beschikt - denk aan een simpele struct met een std::string erin). Maar de stelling klopt niet omdat aggregates non-trivial ctors kunnen hebben, maar toch aan default-initialization doen.
Maar in principe maakt het dus alleen uit voor POD en types die samengesteldzijn uit POD
Ook aggregates dus. Oftewel arrays of classes/structs zonder gespecificeerde ctor, dtor, base class en virtual functions. Een struct met louter een std::string als member is geen POD maar wel een aggregate. Zou je die struct ook een int geven, dan wordt die int bij new met haakjes op 0 geinitialiseerd, maar zonder niet. De string wordt natuurlijk sowieso altijd geinitialiseerd.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Gehakt
  • Registratie: Juli 2002
  • Laatst online: 19-09 15:50
.oisyn schreef op donderdag 03 maart 2011 @ 01:46:
Ook leuk:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

struct Foo
{
    Foo(int) { std::cout << "Foo(int)" << std::endl; }
    Foo(const Foo&) { std::cout << "Foo(const Foo&)" << std::endl; }
};

int main()
{
    Foo f(Foo(2)); // Heb je hier niet stiekem te maken
                   // met een een function type cast?
}

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 03:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Ja, maar een function type cast ís gewoon een aanroep van een single argument ctor (mits die bestaat). Je copy-construct een Foo uit een temporary Foo(2). Het is equivalent aan Foo f((Foo)2);

[ Voor 61% gewijzigd door .oisyn op 03-03-2011 11:42 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1