14 C++ zagadnienia + przenoszenie + obserwer z multithredem + VS


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


 6)      Noexcept   
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.

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 moja;                                      //tworzy obiekt moja z wykorzystaniem konstruktora bezparam.
    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 parametrowy
    Moja 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:
  1. 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 )