standaardparameters in .h- en .cpp-bestanden

COMPILER:g++ 4.7.2

Ok. Dus ik ben in de war over standaardparameters in .h– en .cpp-bestanden. Op veel plaatsen (inclusief deze site) wordt vermeld dat standaardparameters alleen in .h-bestanden kunnen worden toegevoegd en niet in .cpp-bestanden.
Deze code bewijst echter dat het niet klopt:

test1.h

#pragma once
#include <iostream>
using namespace std;
class Class{
public:
    Class(int, int, int=1);
};

test1.cpp

#include "test1.h"
Class::Class(int a, int b=2, int c)
{
    cout<<a<<" "<<b<<" "<<c<<endl;
}
int main()
{
    Class a(1);
    return 0;
}

Nu, volgens wat ik heb getest, kunnen standaardparameters worden toegevoegd aan .cpp-bestanden. De volgende beperkingen gelden echter:

  1. De standaardparameters die aanwezig zijn in het bestand .cppen .hmogen niet
    overlappen. d.w.z. Class(a, b, c=1)(in .h-bestand) en
    Class::Class(a,b,c=2)( in .cpp-bestand) is ongeldig.

    Het is een bekende regel dat als de standaardparameters eenmaal zijn
    toegevoegd, moeten alle variabelen die daarna zijn gedeclareerd ook bevatten:
    standaard waarden. Laten we dit de defpararegel noemen. Nu,

  2. De variabelen vermeld in de functiedeclaratie (bestand .h) moeten
    gehoorzamen aan de defpara-regel, d.w.z. Class(a, b=2, c)(in .h-bestand) is
    ongeldig ongeacht wat er in het .cpp-bestand staat.

  3. Als men bedenkt dat de variabelen standaardwaarden hebben (als een
    kruising van standaardwaarden in .hen .cppbestanden), zou het
    volg de defpararegel. d.w.z. Class(a, b, c=1)(in .h-bestand) en
    Class::Class(a,b=2,c)( in .cppbestand) is geldig. Maar Class(a, b,
    c=1)
    (in .h-bestand) en Class::Class(a=2,b,c)(in .cpp-bestand) is
    ongeldig.

Dus….ik heb gelijk, ongelijk???


Antwoord 1, autoriteit 100%

Standaardwaarden moeten altijd in het headerbestand staan, als de functie in een headerbestand is gedeclareerd.

Dit komt omdat de compiler het header-bestand zal gebruiken voor ALLE compile-eenheden die uw klasse gebruiken [tenzij u “ondeugend” bent en het header-bestand niet overal gebruikt waar het zou moeten gaan].

Aangezien de compiler standaardargumenten toevoegt wanneer hij de code compileert die de functie AANROEP (in dit geval de constructor), maakt het niet uit wat de standaardwaarden zijn in het .cpp-bestand.

Natuurlijk is er in dit geval maar één “gebruiker” van de headerfile, en maar één plaats waar de constructor wordt aangeroepen. Maar het hebben van standaardwaarden in het .cpp-bestand is over het algemeen verkeerd [tenzij het een lokale functie is].

Je krijgt zeer “interessante” bugs als je standaardinstellingen “mixt” – b.v. als uw .cpp één standaard heeft en het hoofdbestand een andere. Als je echt vaardig bent, kun je de compiler zelfs verschillende standaardwaarden laten genereren voor verschillende aanroepen van de functie, wat vrijwel zeker tot kopzorgen zal leiden als de code ervan uitgaat dat de standaard een bepaalde waarde is. En kom niet in de verleiding om de standaardinstellingen van de header naar het .cpp-bestand te kopiëren “gewoon om het gemakkelijker te maken”. Als iemand ooit de standaard verandert, dan zal het vrijwel zeker ofwel niet op beide plaatsen veranderen, en mogelijk nog erger: verander de verkeerde standaardinstellingen, zodat het niet doet wat de bedoeling was.


Antwoord 2, autoriteit 40%

Dit werkt alleen omdat je hoofdfunctie ook in je test.cpp-bestand staat, dus het ziet het standaardargument dat is opgegeven in de implementatie van je klasse. Als u uw main-functie in een apart bestand plaatst dat alleen test.hbevat, wordt deze code niet gecompileerd.

Een andere manier om ernaar te kijken is dat wanneer een andere test.hbevat, de code alleen ziet wat is gedeclareerd in test.h, dus standaardargumenten elders zal niet worden gebruikt.


Antwoord 3, autoriteit 11%

.h vs. .cpp is een rode haring. De regel is dat standaardargumenten kunnen worden gebruikt in functiedeclaraties en in de functiedefinitie. U mag een standaardargument niet opnieuw definiëren, zelfs niet naar dezelfde waarde. Dit is dus niet legaal:

void f(int, int = 3);
void f(int, int = 3); // error: redefinition of default argument

Volgende declaraties kunnen echter standaardargumenten toevoegen:

void f(int, int = 3);
void f(int = 4, int = 3);
f(); // calls f(4, 3);

Verder, op elk punt waar de functie wordt aangeroepen, kunnen de standaardargumenten die op dat moment zijn gezien, worden gebruikt:

void f(int, int =3);
f(1); // calls f(1, 3);
void f(int = 4, int = 3);
f(1); // calls f(1, 3);
f();  // calls f(4, 3);

In het originele voorbeeld definieert het .h-bestand één standaardargument, en elke vertaaleenheid die die header gebruikt, kan dat standaardargument gebruiken:

Class c3(1, 2, 3);
Class c2(1, 2);

Verder definieert het .cpp-bestand een extra standaardargument, dus na die declaratie kan de constructor worden aangeroepen met één, twee of drie argumenten:

Class c3(1, 2, 3);
class c2(1, 2);
class c1(1);

Antwoord 4, autoriteit 3%

Er bestaat niet zoiets als een standaardparameter voor de definitie van een bestand in C++ – het bestaat alleen in de declaratie.

Wat er gebeurt, is dat de compiler een functie ziet die de laatste parameters mist. Als deze standaard zijn, kan het de lege plekken invullen om de objectcode te construeren om de functie aan te roepen alsof de functieaanroep die parameters had.

PS:Item 38/Scott Myers/Effective C++ – Definieer nooit een overgenomen standaardparameterwaarde opnieuw.

Other episodes