Po prostu przez pomyłkę zrobiłem coś takiego w C ++ i to działa. Dlaczego mogę to zrobić?
int main(int argc, char** argv) {
struct MyStruct
{
int somevalue;
};
MyStruct s;
s.somevalue = 5;
}
Po zrobieniu tego trochę przypomniałem sobie, że czytałem o tej sztuczce gdzieś, dawno temu, jako rodzaj funkcjonalnego narzędzia programistycznego dla biednych dla C ++, ale nie pamiętam, dlaczego to jest ważne, ani gdzie to przeczytałem.
Odpowiedzi na którekolwiek z pytań są mile widziane!
Uwaga: Chociaż pisząc pytanie, nie otrzymałem żadnych odniesień do tego pytania , obecny pasek boczny wskazuje na to, więc umieszczę je tutaj jako odniesienie, tak czy inaczej pytanie jest inne, ale może być przydatne.
c++
data-structures
functional-programming
Robert Gould
źródło
źródło
Odpowiedzi:
[EDYCJA 18.04.2013]: Na szczęście ograniczenie wymienione poniżej zostało zniesione w C ++ 11, więc lokalnie zdefiniowane klasy są w końcu przydatne! Podziękowania dla komentatora Bamboon.
Możliwość definiowania klas lokalnie byłoby zrobić tworzenia niestandardowych funktory (zajęcia z
operator()()
np porównawczych dla funkcji przechodząc dostd::sort()
lub „Pętla” organów do stosowania zstd::for_each()
) znacznie wygodniejsze.Niestety, C ++ zabrania używania lokalnie zdefiniowanych klas z szablonami , ponieważ nie mają one żadnego powiązania. Ponieważ większość aplikacji funktorów obejmuje typy szablonów, które są szablonami na typie funktora, nie można do tego użyć klas zdefiniowanych lokalnie - należy je zdefiniować poza funkcją. :(
[EDYCJA 11.01.2009]
Odpowiedni cytat z normy to:
źródło
Jedno zastosowanie lokalnie zdefiniowanych klas C ++ znajduje się we wzorcu projektowym Factory :
// In some header class Base { public: virtual ~Base() {} virtual void DoStuff() = 0; }; Base* CreateBase( const Param& ); // in some .cpp file Base* CreateBase( const Params& p ) { struct Impl: Base { virtual void DoStuff() { ... } }; ... return new Impl; }
Chociaż możesz zrobić to samo z anonimową przestrzenią nazw.
źródło
W rzeczywistości jest to bardzo przydatne do wykonywania pewnych prac związanych z bezpieczeństwem wyjątków opartych na stosie. Lub ogólne czyszczenie funkcji z wieloma punktami powrotu. Jest to często nazywane idiomem RAII (pozyskiwanie zasobów to inicjalizacja).
void function() { struct Cleaner { Cleaner() { // do some initialization code in here // maybe start some transaction, or acquire a mutex or something } ~Cleaner() { // do the associated cleanup // (commit your transaction, release your mutex, etc.) } }; Cleaner cleaner; // Now do something really dangerous // But you know that even in the case of an uncaught exception, // ~Cleaner will be called. // Or alternatively, write some ill-advised code with multiple return points here. // No matter where you return from the function ~Cleaner will be called. }
źródło
Cleaner cleaner();
Myślę, że będzie to raczej deklaracja funkcji niż definicja obiektu.Cleaner cleaner;
lubCleaner cleaner{};
.Cóż, w zasadzie, czemu nie? A
struct
w C (cofając się do zarania czasu) było tylko sposobem na zadeklarowanie struktury rekordu. Jeśli chcesz, dlaczego nie móc zadeklarować go w miejscu, w którym zadeklarowałbyś prostą zmienną?Kiedy już to zrobisz, pamiętaj, że celem C ++ było być kompatybilne z C, jeśli to w ogóle możliwe. Tak zostało.
źródło
Wspomniano o tym na przykład w sekcji „7.8: Klasy lokalne: klasy wewnątrz funkcji” witryny http://www.icce.rug.nl/documents/cplusplus/cplusplus07.html, która nazywa ją „klasą lokalną” i mówi o tym „ może być bardzo przydatny w zaawansowanych aplikacjach obejmujących dziedziczenie lub szablony ”.
źródło
Służy do tworzenia tablic obiektów, które są odpowiednio zainicjalizowane.
Mam klasę C, która nie ma domyślnego konstruktora. Chcę mieć tablicę obiektów klasy C.Wymyślam, jak chcę, aby te obiekty były inicjalizowane, a następnie wyprowadzam klasę D z C za pomocą metody statycznej, która dostarcza argument dla domyślnego konstruktora C w D:
#include <iostream> using namespace std; class C { public: C(int x) : mData(x) {} int method() { return mData; } // ... private: int mData; }; void f() { // Here I am in f. I need an array of 50 C objects starting with C(22) class D : public C { public: D() : C(D::clicker()) {} private: // I want my C objects to be initialized with consecutive // integers, starting at 22. static int clicker() { static int current = 22; return current++; } }; D array[50] ; // Now I will display the object in position 11 to verify it got initialized // with the right value. cout << "This should be 33: --> " << array[11].method() << endl; cout << "sizodf(C): " << sizeof(C) << endl; cout << "sizeof(D): " << sizeof(D) << endl; return; } int main(int, char **) { f(); return 0; }
Ze względu na prostotę w tym przykładzie zastosowano trywialny konstruktor niedomyślny i przypadek, w którym wartości są znane w czasie kompilacji. Technikę tę można w prosty sposób rozszerzyć na przypadki, w których chcesz, aby tablica obiektów została zainicjowana wartościami, które są znane tylko w czasie wykonywania.
źródło
D*
parametr), to po cichu się zepsuje, jeśli D jest faktycznie większy niż C . (Myślę ...)