Zagadnienia
1) Różnica pomiędzy listą inicjalizacyjna a
listą inicjalizatorów (Initialization list -
std::Initializer_list)
- Lista inicjalizatorów jest listą wartoości początkowych.
- Lista incjalizacyjna to lista operacji (czynnoości) inicjalizacyjnych,
które ma wykonać konstruktor.
Przykłady:
- Przykład dwóch list inicjalizatorów:
initializer_list<int> a { 40, 30, 222 };
auto b { 1.2, 3.14, 55.5 };
- Przykład listy inicjalizacyjnej konstruktora :
klasa::klasa(argumenty) : lista( inicjalizacyjna){ }
2) static_assert
Narzędzie wprowadza nowy sposób testowania asercji w czasie
kompilacji, przy użyciu nowego słowa kluczowego static_assert. Deklaracja
przyjmuje następującą formę:
static_assert( stałe_wyrażenie, komunikat_błędu ) ;
Zadaniem deklaracji static_assert jest upewnienie się, że
wartoość tego wyrażenia to true.
Czyli upewnienie się, że ten warunek jest spełniony. Jeśli
jest spełniony, to nie dzieje się nic nadzwyczajnego. Kompilator pracuje dalej.
Jeśli wartością
wyrażenia bool_constexpr jest false, to na ekranie pojawia się komunikat o
błędzie kompilacji, któremu towarzyszy przygotowany przez nas “komunikat błędu”
3) constexpr (wyrażenia stałe)
W C++ stałe wyrażenia to wyrażenia takie jak 3+4, które zawsze zwracają
ten sam wynik i nie wywołują żadnych dodatkowych efektów ubocznych (ang. side
effect). Stałe wyrażenia są dla kompilatorów okazją do optymalizacji,
ponieważ kompilatory często wykonują te wyrażenia w czasie kompilacji i
wstawiają ich wyniki do programu. Jest wiele miejsc, gdzie specyfikacja C++
wymaga użycia stałych wyrażeń. Są to między innymi definicja tablicy i wartości
typów wyliczeniowych. Jednak jeśli wyrażenie zawiera wywołanie funkcji lub
wykonanie konstruktora obiektu nie będzie zinterpretowane przez kompilator jako
stałe. Na przykład:
int GetFive() {return 5;}
int someValues[GetFive() + 5]; //stwórz tablicę 10
elementów typu ''int''. Jest to błąd w C++.
Zostaje wykryty błąd, ponieważ GetFive() + 5 nie
jest wyrażeniem stałym. Kompilator nie potrafi rozpoznać, że GetFive jest stałe w
czasie uruchamiania, ponieważ w teorii ta funkcja mogłaby wpływać na globalną
zmienną, wywołać funkcje nie będące stałe w czasie uruchamiania, itd.
C++11 wprowadza nowe słowo kluczowe constexpr, które pozwala
użytkownikowi na zagwarantowanie, że funkcja lub konstruktor obiektu są stałymi
podczas kompilacji. W C++11 powyższy kod można przepisać następująco:
constexpr int GetFive() {return 5;}
int someValues[GetFive() + 5]; //stwórz tablicę 10
elementów typu ''int''. Poprawne w C++11
To pozwala kompilatorowi rozpoznać i zweryfikować, że GetFive jest stałą
podczas kompilacji.
Słowo kluczowe constexpr gwarantuje, że wartość zwracana
przez funkcję, metodę bądź zmienną jest stała podczas procesu kompilacji. Są to
funkcje, które (spełniając pewne warunki) otrzymują prawo do występowania również
w wyrażeniach inicjalizujących obiekty z przydomkiem constexpr. Funkcje
constexpr tworzymy po to, aby się nimi natychmiast posłużyć do definiowania
innych obiektów constexpr.
constexpr Clasa nazwaNowejKlasy = obiekt.metodaa( 88,77); //metodaa musi być constexpr
Użycie constexpr narzuca pewne ograniczenia na budowę
funkcji. Ograniczenia te są następujące:
-funkcja musi zwracać wartość, więc nie może zwracać typu
void;
-funkcja nie może deklarować żadnych zmiennych ani
definiować nowych typów danych;
- ciało funkcji może zawierać tylko pojedynczą instrukcję
return;
- argumenty funkcji muszą gwarantować, że po ich
podstawieniu będzie możliwe otrzymanie wyrażenia o stałej wartości.
Zmienne jako
stałowyrażeniowe: ( stosowane: zamiast makr obiektowych)
Standard C++11 wprowadził również możliwość definiowania
zmiennych ze słowem kluczowym constexpr. Zmienne, które używają słowa
kluczowego constexpr muszą zostać zainicjalizowane podczas ich tworzenia
wartością stałą lub wyrażeniem, które da wartość stałą:
constexpr int DANGEROUS = 1024 + 1024;
stosuj zamiast makr obiektowych #define DANGEROUS 1024+1024
4) reguła pięciu a reguła zera
historyczne reguły (przed c++ 11) reguła trzech: jeśli w
klasie bezpośrednio zdefiniowany jest destruktor, prawie zawsze należy
zdefiniować też konstruktor kopiujący i kopiujący operator przypisania.
Reguła pięciu (od c++11) : do języka dodano konstruktor
przenoszący i przenoszący operator przypisania, dlatego w klasie z definicją
destruktora trzeba było definiować także te dwie specjalne funkcje składowe.
(najnowsze podejście)
głównym celem każdego dewelopera używającego C++
powinno być to, aby nie deklarować i definiować
bezpośrednio tego rodzaju konstruktorów oraz operatorów przypisania. Treść
reguły zera:
Pisz klasy w taki sposób, abyś nie musiał
bezpośrednio deklarować i definiować ani destruktora, ani konstruktorów
kopiujących i przenoszących, ani kopiujących i przenoszących operatorów
przypisania. Do zarządzania zasobami stosuj inteligentne wskaźniki języka C++
oraz klasy i kontenery z biblioteki standardowej.
5) Volatile
Zmienna ulotna – w programowaniu, słowo kluczowe volatile
oznacza, że zmienna lub obiekt mogą zostać zmienione "z zewnątrz" —
niezależnie od kodu programu, w którym się znajdują. Pomiędzy różnymi
odczytami, wartości zmiennej mogą być różne, nawet jeśli nie były zmodyfikowane
w kodzie. Zastosowanie volatile powstrzymuje kompilator optymalizujący przed
pomijaniem zapisów do pamięci lub w wypadku kolejnych odczytów lub zapisów
zmiennej przed zastąpieniem jej w skompilowanym kodzie przez stałą. Zmienne
ulotne pojawiają się przede wszystkim w dostępie do sprzętu, gdzie korzystanie
z pamięci jest wykorzystywane do komunikacji pomiędzy urządzeniami.
Pytanie dodatkowe:
Czy nadaje się do zastosowania w środowisku wielowątkowym, w
którym różne wątki mogą korzystać z tej samej zmiennej? (takie zastosowanie
sugeruje Wikipedia)
NIE!
Stosuj std::atomic
dla współbieżności, volatile dla
pamieci specjalnej
Mechanizm obsługi sytuacji wyjątkowych jest innym, nowym
sposobem wyjścia z funkcji. Teraz więc – przy wywołaniu danej funkcji –
kompilator musi przygotować kod zdolny obsłużyć również i taki sposób powrotu z
funkcji. Jest jednak sposób uprzedzenia kompila tora, żeby nie robić tych
przygotowań wobec wybranej funkcji, ponieważ:
- dana funkcja nie rzuca wyjątków,
- jeśli nawet rzuca,
to nie zamierzamy ich obsługiwać
Kompilator może wtedy uprościć i zoptymalizować kod
wywołania danej funkcji.
void fun1() noexcept;
// noexcept jest równowaźne
noexcept(true)
7) Explicit
Żeby kompilator nie używał danego
konstruktora do niejawnych zamian (konwersji), to wystarczy przed deklaracją danego
konstruktora postawić słowo kluczowe explicit.
class K {
public:
explicit K(int wartosc) { }
// konstruktor
};
void funkcja(K obiekt) { }
int main (){
K obiektK(3);
funkcja(obiektK); // zwykłe użycie funkcji
// To poniższe użycie wymaga
zamiany liczby 4 na obiekt klasy K.
// Uda się, gdy jest to
konstruktor bez słowa explicit.
funkcja(4); // Błąd, jeśli
odpowiedni konstruktor klasy K jest explicit.
}
8)
Konstruktor
przenoszący i semantyka przenoszenia
Konstruktor przenoszący konstruuje obiekt, korzystając z
innego, pokazanego mu obiektu tej samej klasy.
Konstruktor kopiujący traktuje pokazany mu obiekt za wzór,
którego nie wolno zmienić.
Konstruktor przenoszący traktuje pokazany mu obiekt jako
coś, co może poddać zmianie. Budując swój nowy obiekt, może wykorzystać
niektóre zasoby ze starego. Wie, że przysłany do niego wzór nie będzie więcej
używany co może przyśpieszyć tworzenie nowego obiektu.
Konstruktor przenoszący powinien wykonać pracę w takich etapach:
- Krok 1. Skopiować dane składowe ze starego obiektu i
równoczeoenie przejmując zasoby
(starego) wzorcowego obiektu.
- Krok 2. Składniki starego (wzorcowego) obiektu, które
dotyczą przejętych od niego zasobów, ustawić w takim stanie, żeby wiedział on,
że już nie posiada tych zasobów, i żeby nie próbował na nich więcej pracować.
Pytanie
dodatkowe: (znajomość Copy elision )
Czy używać
przenoszenia do zwracania przez funkcję lokalnego obiektu czy zwracać przez
wartość ?
Mechanizm przenoszenia rzeczywiście robi to sprawnie, ale
jest coś jeszcze sprawniejszego. To technika RVO, w
standardzie C++11 zwana pomijaniem kopiowania (ang. Copy elision).
Jest ono jeszcze lepsze niż przenoszenie i kompilatory
stosują ten sposób, kiedy tylko mogą.
Zatem lokalny obiekt funkcji zwracaj przez wartość, nawet jeśli
ma on wbudowany mechanizm przenoszenia. Nie dodawaj mu żadnego rzutowania
std::move(obiekt), bo tylko utrudnisz kompilatorowi pracę.
9) SOLID
(założenia programowania obiektowego)
Single
responsibility - Klasa powinna mieć tylko jedną odpowiedzialność
Open - Klasy powinny być otwarte na rozszerzenia i
zamknięte na modyfikacje.
Liskov
podstawianie - Funkcje z wskaźnikami (referencjami) do klas bazowych, muszą być
w stanie używać również obiektów klas dziedziczących po klasach bazowych (bez
ich znajomości)
Interface
segregation - Wiele dedykowanych interfejsów jest lepsze niż jeden ogólny
Dependency
inversion - Wysokopoziomowe moduły nie powinny zależeć od modułów
niskopoziomowych. Tworzymy interfejsy których używają wysokopoziomowe i
wstrzykujemy wysokim w konstruktorze adres obiektu niskiego poziomu. Dzięki
czemu wysoki nie jest zależna od konkretnego niskiego ale zależy od abstrakcji
(interfejs). Abstrakcja reprezentowana przez zmienna składową inicjalizowaną
konstruktorem.
................................
konstruktory a semantyka przenoszenia
dla c++11 istnieje reguła 5:
(stary c++) reguła trzech: jeśli w klasie bezpośrednio zdefiniowany jest destruktor, prawie zawsze należy zdefiniować też konstruktor kopiujący i kopiujący operator przypisania.
Reguła pięciu (od c++11) : do języka dodano konstruktor przenoszący i przenoszący operator przypisania, dlatego w klasie z definicją destruktora trzeba było definiować także te dwie specjalne funkcje składowe.
Reguła pięciu (od c++11) : do języka dodano konstruktor przenoszący i przenoszący operator przypisania, dlatego w klasie z definicją destruktora trzeba było definiować także te dwie specjalne funkcje składowe.
choć można podejść do tego tak(reguła zera):
Pisz klasy w taki sposób, abyś nie musiał bezpośrednio deklarować i definiować ani destruktora, ani konstruktorów kopiujących i przenoszących, ani kopiujących i przenoszących operatorów przypisania. Do zarządzania zasobami stosuj inteligentne wskaźniki języka C++ oraz klasy i kontenery z biblioteki standardowej.
***
kiedy jakie konstruktory są uruchamiane ?
konstruktor kopiujący (ewentualnie przenoszący) uruchamiane są jeśli przypisujesz jako wartość inny obiekt w momencie tworzenia np:
Moja* ptrmoja=new Moja(moja); //tworzy obiekt za pomoca konstruktora KOPIUJACEGO
czyli tworzymy nowy obiekt na podstawie danych z innego.
operator przypisania = (ewentualnie przenoszący op.przypisania) uruchamia się jeśli przypisujesz później niż w chwili tworzenia obiektu
Moja moja; //tworzy obiekt moja z wykorzystaniem konstruktora bezparam.
moja = Moja("aa"); //przypisuje do istniejącego obiektu wartości ze stworzonego po prawej stronie
do istniejącego obiektu przypisujemy dane z innego
Przykład:
klasy.hpp
#include<iostream> #include<string> namespace std { class Moja { public: Moja(); Moja(string a); ~Moja(); Moja(Moja&); // konstruktor kopiujacy Moja(Moja&&); //konstruktor przenoszacy Moja& operator=(const Moja&); //operator przypisania = Moja& operator=(Moja&&); //przenoszący operator przypisania = void printt(); private: string mojs; }; //**************************************** MM**************************************** class MM { public: MM(); ~MM(); void pp(); private: Moja moja=Moja("aa"); //uruchomi tylko konstruktor parametrowy //Moja moja; // uruchomi tylko konstruktor bezparametrowy Moja* ptrmoja=new Moja(moja); //uruchomi tylko konstruktor KOPIUJACY }; } // namespace
plik.cpp
#include<iostream> #include<string> #include <memory> #include"klasy.hpp" namespace std { Moja::Moja():mojs("przykladowy tekst") { cout<< " konstruktor bezparam"<<endl; } Moja::Moja(string a) { mojs=a; cout<< " konstruktor parametrowy"<<endl; } Moja::~Moja() {} Moja::Moja(Moja& other) //konstruktor kopiujacy { //Konstruktor kopiujący uruchamia się jeśli przypisujesz wartośc cout<<"KOPIUJACY"<<endl; //w chwili tworzenia obiektu mojs=other.mojs; } Moja::Moja(Moja&& other) //konstruktor przenoszacy (gdy uzywasz std::move ) { cout<<"PRZENOSZACY"<<endl; mojs=other.mojs; other.mojs='\0'; } Moja& Moja::operator=(const Moja& other) //operator przypisania , jeśli przypisujesz później niż { //w chwili tworzenia obiektu, chyba ze jest przenoszacy cout<<"OPERATOR PRZYPISANIA ="<<endl; mojs=other.mojs; return *this; } Moja& Moja::operator=(Moja&& other) // przenoszący operator przypisania jeśli przypisujesz później niż { //w chwili tworzenia obiektu, jesli operator stworzony to uzywany ten! cout<<"przenoszacy operator przypisania"<<endl; if (this != &other) { mojs=other.mojs; delete(&other); } return *this; } void Moja::printt() { cout<<mojs<<endl; } //************************************** MM ******************************** MM::MM() { //moja = Moja("aa"); //dodanie tego sprawi, że prawa strona uruchomi konstruktor parametrowy //znak przypisania uruchomi "przenoszący operator przypisania" //jeśli jest, jeśli nie, uruchomi zwykły operator przypisania //ptrmoja=new Moja("pp"); //tu pomimo operatora przypisania, następuje tylko przypisanie wskaźnika // wiec nie uruchomi sie przenoszący operator przypisania //moja=*ptrmoja; //to natomiast uruchomi operator przypisania (nie przenoszący) dlatego, że //obiekt *ptrmoja nie zniknie po tej operacji (prawa strona nie jest r-wartoscia) moja=Moja(std::move (Moja("pp"))); //move uruchamia semantykę przenoszenia czyli przenoszacy } MM::~MM() {} void MM::pp() { moja.printt(); ptrmoja->printt(); } }// namespace
i uruchomienie w main
MM obiekt;
obiekt.pp();
da wynik:
konstruktor parametrowy // to i...
KOPIUJACY // to obszar private deklaracji pól (hpp) klasy MM
konstruktor parametrowy // to1
PRZENOSZACY // to2
przenoszacy operator przypisania //to3 działanie linii z konstruktora MM
pp // moja=Moja(std::move (Moja("pp")));
aa
Opis przykładu:
obiekt moja klasy Moja tworzony w innej klasie MM w obszarze deklaracji jako zmienna(pole) (plik hpp)
private:
Moja moja=Moja("aa"); //uruchomi tylko konstruktor parametrowyMoja moja; // uruchomi tylko konstruktor bezparametrowy (wybierz jeden)
ale jeśli mamy już stworzony wyżej obiekt moja, to następujące polecenie:
Moja* ptrmoja=new Moja(moja); //uruchomi tylko konstruktor KOPIUJACY
Jeśli następnie dodamy kolejne polecenia w konstruktorze klasy która tworzy te obiekty to:
1)
moja = Moja("aa");
dodanie tego sprawi, że prawa strona uruchomi konstruktor parametrowy
znak przypisania = uruchomi "przenoszący operator przypisania" jeśli jest przeciążony, jeśli nie, uruchomi zwykły operator przypisania, bo obiekt moja już istnieje.
2)
ptrmoja=new Moja("pp");
tu pomimo operatora przypisania, następuje TYLKO uruchomienie konstruktora parametrowego i skopiowanie adresu. Nie ma uruchomiania konstruktorów kopiujących ani operatora przypisania ponieważ nastąpiło tylko przypisanie wskaźnika (pomimo, że pod ptrmoja mieliśmy wcześniej adres innego obiektu, adres został nadpisany)
3)
moja=*ptrmoja;
to natomiast uruchomi operator przypisania (nie przenoszący o.p.! dlatego, że obiekt *ptrmoja nie zniknie po tej operacji, prawa strona nie jest r-wartoscia)
4)
moja=Moja(std::move (Moja("pp")));
move uruchamia semantykę przenoszenia czyli dopiero to uruchomi konstruktor przenoszący (prawa strona) samo przypisanie jak w pierwszym przypadku dodatkowo uruchomi przenoszący operator przypisania
obserwer z multithredem
Przykład wzorca projektowego obserwer z multithredem na c++11
r1.cpp
#include<iostream> #include<vector> #include<list> #include<algorithm> #include<stdlib.h> #include <thread> #include <string> using namespace std; // EXAMPLE OBSERWER + multithread class Obserwator { public: virtual void update()=0; }; class Obserwowany { protected: std::list <Obserwator*> obserwatorzy; public: void dodaj(Obserwator *o) { obserwatorzy.push_back (o); } void usun(Obserwator *o) { obserwatorzy.remove (o); } void powiadom () { std::list<Obserwator *>::iterator it; for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) { (*it)->update (); } } }; class Student: public Obserwowany { public: int id; int get_id() {return id;}; void set_id(int value) {id=value;}; void pracuj () { int licz=100; while (licz--) { id+=1; powiadom(); //powiadom obserwatora std::this_thread::sleep_for(std::chrono::milliseconds(100)); // cout<<id<<endl; //wyswietlanie przekazane do obserwatora } }; Student(unsigned int init_val) {id = init_val;}; // constructor ~Student() {}; // destructor }; class Odczyt:public Obserwator { int id1; protected: int obId; //zapisz id obiektu ktorym jestes Student *obiekt; //zapisz wskaznik na obserwowany obiekt public: Odczyt (Student *h, int id) { //konstruktor (wskaznik na obserwowany, od ktorym jestes) obiekt = h; obId = id; } void update (){ //twoja funkcja ktora wywoluje obserwowany id1=obiekt->get_id(); cout<<id1<<endl; } int get_id() {return id1;}; void set_id(int value) {id1=value;}; }; std::vector<int> vectorint; std::vector<char> vectorchar; int main() { Student* mojstudent=new Student(10); //stworzenie obiektu obserwowanego Obserwator* mis1=new Odczyt(mojstudent,1); //stworzenie obserwatora mojstudent->dodaj(mis1); //zarejestrowanie obserwatora //watki w c++ std::thread th(&Student::pracuj, mojstudent); //th.join(); //mojstudent->pracuj(); while(1) { cout<<"odliczanie:" <<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return 0; }
Przykład interfejsow z multithreadem
Student i Profesor to Obserwatory - mają metodę (interfejs) dziedziczoną z klasy Interface
Komponent1 to Obserwowany, po stworzeniu obiektów trzeba w nim zarejestrować Obserwatorów.
Teoretycznie obserwatorzy podczas tworzenia powinni mieć (przez konstruktor) podawany adres Obserwowanego (żeby mieć możliwość na sygnał z interfejsu wywołać jego metody)
Tu obserwatorzy ,ają tylko wywoływany interfejs, w następnym przykładzie przykład przekazania do nichadresu obserwowanego podczas ich rejestracji w Obserwowanym.
r5.cpp
#include<iostream> #include<vector> #include<list> #include<algorithm> #include<stdlib.h> #include <thread> #include <string> using namespace std; // EXAMPLE komponent - INTERFACE - dependency komponent + MULTITHREAD // odwrotnie interface -> interfacedep class Interface { public: virtual void pracuj()=0; }; class Student: public Interface //interface obserwator, student's method pracuj is runing by interface { public: int id=0; void pracuj () { int licz=10; while (licz--) { id+=1; cout<<id<<endl; // std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }; }; class Profesor: public Interface //second interface obserwator, Profesor's method pracuj is runing by interface { public: int id=0; void pracuj () { int licz=10; while (licz--) { id-=1; cout<<id<<endl; // std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }; }; class InterfaceDep { //InterfaceDep for Obserwowany (remeber list of obserwator object) protected: std::list <Interface*> obserwatorzy; public: void dodaj(Interface *o) { obserwatorzy.push_back (o); } void usun(Interface *o) { obserwatorzy.remove (o); } }; class Komponent1:public InterfaceDep //komponent with interfaceDep { public: void inform() { std::list<Interface *>::iterator it; for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) (*it)->pracuj (); } void wyswietl() { int licz=48; bool flag=1; while(1) { if (flag) { if(licz<50) { licz++; if(licz==49) flag=0; } } else { if(licz>0) { licz--; if(licz==1) flag=1; } } for(int i=0;i<licz;i++) cout<<"XX"; cout<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void pracuj() { while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); this->inform(); } } }; int main() { Komponent1* mojkomponent=new Komponent1(); //create ob Komponentu1 Obserwowany Interface* mojstudent=new Student(); //create ob Student Obserwator Interface* mojprofesor=new Profesor(); //create ob Profesor Obserwator
mojkomponent->dodaj(mojstudent); //register Obserwator object in komponent with interface list mojkomponent->dodaj(mojprofesor); //register Obserwator object in komponent with interface list std::thread th(&Komponent1::pracuj, mojkomponent); std::thread th2(&Komponent1::wyswietl, mojkomponent); int licz=0; bool flag=1;
while(1) { if (flag) { if(licz<50) { licz++; if(licz==49) flag=0; } } else { if(licz>0) { licz--; if(licz==1) flag=1; } } for(int i=0;i<licz;i++) cout<<"*"; cout<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return 0; }
Przykład wzajemnej rejestracji adresów.
r6.cpp
#include<iostream> #include<vector> #include<list> #include<algorithm> #include<stdlib.h> #include <thread> #include <string> using namespace std; // EXAMPLE komponent - INTERFACE - dependency komponent // wzajemne rejestrowanie obserwatora i obserwowanego podczas dodawania class Interface1; //Obserwowany Interface Forward declaration dla pointera class Interface01 { //obserwator interface public: Interface1 *Ob; //pointer do obserwowanego virtual void pracuj()=0; }; class Interface1 { //Obserwowany Interface (remeber list of obserwing object) protected: std::list <Interface01*> obserwatorzy; public: virtual void spiewaj()=0; //wirtualna metoda wywolywana przez obserwatora void dodaj(Interface01 *o) { obserwatorzy.push_back (o); o->Ob=this; //wpisuje adres obserwatora i podaje mu swoj adres do jego zmiennej Ob } void usun(Interface01 *o) { obserwatorzy.remove (o); } }; class DrStudent: public Interface01 //Obserwator with interface , student's method pracuj is runing by interface { public: int id=0; void pracuj () { int licz=10; while (licz--) { id+=1; cout<<id<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } Ob->spiewaj(); }; }; class DrProfesor: public Interface01 //Obserwator with second interface , Profesor's method pracuj is runing by interface { public: int id=0; void pracuj () { int licz=10; while (licz--) { id-=1; cout<<id<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } Ob->spiewaj(); }; }; class Komponent1:public Interface1 //komponent obserwowany with interface { public: void spiewaj() { cout<<"TRALALALLALALALALALLA"<<endl; } void inform() { std::list<Interface01 *>::iterator it; std::vector<std::thread> myThreads; for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) { (*it)->pracuj (); // myThreads.push_back(std::thread(&Interface01::pracuj, *it)); //nie dziala } } }; int main() { Komponent1* mojkomponent=new Komponent1(); //create obserwowany Interface01* mojstudent=new DrStudent(); //create obserwator Student in Interface01* mojprofesor=new DrProfesor(); //create obserwator Profesor in mojkomponent->dodaj(mojstudent); //register next obserwator object in obserwiowany list mojkomponent->dodaj(mojprofesor); //register next obserwator object in obserwowany list //watki w c++ //std::thread th(&Student::pracuj, mojstudent); //th.join(); while(1) { cout<<"odliczanie:" <<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); mojkomponent->inform(); //wysylaj powiadomienie do obserwatorow } return 0; }
Wersja programu r5 (interfejsy bez wzajemnego rejestrowania) ale na POSIX multithread.
Nie można bezpośrednio uruchomić metody obiektu, tu wersja z dodaną metodą statyczną która wywołuje właściwą. Następny przykład będzi z rzutowaniem funkcji.
r15.cpp
#include<iostream> #include<vector> #include<list> #include<algorithm> #include<stdlib.h> #include <thread> #include<pthread.h> #include <string> #include<unistd.h> // sleep using namespace std; // Multithread wersja POSIX dodanie STATYCZNEJ metody // EXAMPLE komponent - INTERFACE - dependency komponent + MULTITHREAD // odwrotnie interface -> interfacedep class Interface { public: virtual void pracuj()=0; }; class Student: public Interface //interface obserwator, student's method pracuj is runing by interface { public: int id=0; void pracuj () { int licz=10; while (licz--) { id+=1; cout<<id<<endl; // std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }; }; class Profesor: public Interface //second interface obserwator, Profesor's method pracuj is runing by interface { { public: int id=0; void pracuj () { int licz=10; while (licz--) { id-=1; cout<<id<<endl; // std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }; }; class InterfaceDep { //InterfaceDep for Obserwowany (remeber list of obserwator object) protected: std::list <Interface*> obserwatorzy; public: void dodaj(Interface *o) { obserwatorzy.push_back (o); } void usun(Interface *o) { obserwatorzy.remove (o); } }; class Komponent1:public InterfaceDep //komponent with interfaceDep { public: void inform() { std::list<Interface *>::iterator it; for (it = obserwatorzy.begin(); it != obserwatorzy.end(); it++) (*it)->pracuj (); } void *wyswietl() { int licz=48; bool flag=1; while(1) { if (flag) { if(licz<50) { licz++; if(licz==49) flag=0; } } else { if(licz>0) { licz--; if(licz==1) flag=1; } } for(int i=0;i<licz;i++) cout<<"XX"; cout<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); //sleep(1); } } void *pracuj() { while(1) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); // sleep(1); inform(); } } // dodanie metod statycznych (i zamiana metod pracuj i wyswietl na wskaznikowe funkcje) // https://stackoverflow.com/questions/1151582/pthread-function-from-a-class static void *pracujss(void *context) { return ((Komponent1 *)context)->pracuj(); } static void *wyswietlss (void *context) { return ((Komponent1 *)context)->wyswietl(); } }; int main() { Komponent1* mojkomponent=new Komponent1(); //create ob Komponentu1 Obserwowany Interface* mojstudent=new Student(); //create ob Student Obserwator Interface* mojprofesor=new Profesor(); //create ob Profesor Obserwator mojkomponent->dodaj(mojstudent); //register Obserwator object in komponent with interface list mojkomponent->dodaj(mojprofesor); //register Obserwator object in komponent with interface list //zamiana watkow c++11 na POSIX STATYCZNE metody //std::thread th(&Komponent1::pracuj, mojkomponent); //std::thread th2(&Komponent1::wyswietl, mojkomponent); pthread_t thread_id; pthread_t thread_id2; //POSIX nie potrafi wywolac metod obiektow, wiec wywoluje statyczne pthread_create(&thread_id,NULL,&Komponent1::pracujss,mojkomponent); pthread_create(&thread_id2,NULL,&Komponent1::wyswietlss,mojkomponent); int licz=0; bool flag=1; while(1) { if (flag) { if(licz<50) { licz++; if(licz==49) flag=0; } } else { if(licz>0) { licz--; if(licz==1) flag=1; } } for(int i=0;i<licz;i++) cout<<"*"; cout<<endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); //sleep(1); } return 0; }
wersja z rzutowaniem
(void *(*)(void *)) lub stworzony: typedef void * (*THREADFUNCPTR)(void *);
LINK git
Wersja z rzutowaniem niedoskonała, nie mozna wywołać metod prywatnych.
Struktura plików i katalogow oraz wykorzystanie interfejsow i klasy z ENUM error code
Link Git (folders)https://github.com/JanosikOpryszek/kod/tree/master/proj002
Każdy katalog to osobna warstwa abstrakcyjna, w każdym katalogu klasa.
W katalogu publ klasy interfejsow (ktore dziedzicza inne klasy w celu zaimplementowania)
W tam katalogu jest też klasa Errorcode która zawiera spis błędów typu ENUM.
CMAKE wersja z thread c++11
cmake_minimum_required (VERSION 2.6)
project (Test)
SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11 -pthread")
find_package (Threads)
add_executable (main src/main.cpp)
target_link_libraries (main ${CMAKE_THREAD_LIBS_INIT})
Przeciążanie NEW
The three standard global new operators are as follows:
:: operator new(std::size_t size) throw(std::bad_alloc) /* operator new.
* It throws exceptions if the operation fails.*/
:: operator new(std::size_t size, const std::nothrow_t &) throw() /* nothrow new.
* It does not throw exceptions, but returns NULL.*/
Furthermore there are three array new operators:
:: operator new[](std::size_t size) throw(std::bad_alloc)
:: operator new[](std::size_t size, const std::nothrow_t &) throw()
To overload the new operators you need to take care:
- If you write a non-normal form placement new, you need to write its corresponding delete. For example:
Class A{
public:
...
static void * operator new(std::size_t size, std::ostream& logStream)
throw(std::bad_alloc); // non-normal form placement new
static void * operator delete(void *pMemory, std::ostream& logStream)
throw(); // corresponding delete
};
Link
Inna wersja:
replaceable non-throwing allocation functions
void* operator new ( std::size_t count, const std::nothrow_t& tag);
| (5) | |
void* operator new[]( std::size_t count, const std::nothrow_t& tag);
| (6) |
Link
TCP i UDP pod linuxem
http://cpp0x.pl/artykuly/?id=66
https://4programmers.net/Forum/C_i_C++/245063-client
https://www.abc.se/~m6695/udp.html
https://www.geeksforgeeks.org/udp-server-client-implementation-c/
LINK do notatek
VS
lokalizacja pliku konfiguracyjnego do VSCode:
Windows %APPDATA%\Code\User\settings.json.
Enhancing Visual Studio with Visual Assist (Filipiuk Bloog)
Top 10 features that improve productivity (VISUAL ASSIST )