Le langage C++

De Wikip
Révision datée du 31 janvier 2023 à 20:29 par toross>WikiAdmin (→‎Voir aussi)
Version : 1.36.1 4508 (2023-01-31) 20230131202915
Auteurs :
Arthur TOROSSIAN
Mots clés :
langage
Résumé :
Cette page contient les connaissances concises sur le langage C++. Elle peut servir de tutoriel minimaliste ou d'un mini référentiel.

1 Références

[R1] Changkun Ou, (March 26, 2022), «  Modern C++ Tutorial: C++11/14/17/20 On the Fly   », Washington, ‎Creative Commons, 89 pages, [1]pdf , ✑.

2 Liens externes

3 Voir aussi

4 La fonction main

int main(void); 
int main();
int main(int argc, char *argv[]);

5 La gestion du temps

5.1 Attendre

5.1.1 sleep

#include <time.h>
 
void sleep( time_t nb_sec  )
{ 
    time_t limit, top; 
    time(&top);
    limit = top + nb_sec ; 
    while (top < limit)
    {
        time(&top);
    }
};

5.1.2 Exemple

#include <time.h>
#include <stdio.h>
void sleep( time_t nb_sec  )
{
    time_t limit, top;
    time(&top);
    limit = top + nb_sec ;
    while (top < limit)
    {
        time(&top);
    }
};
int main( int argc, char * argv[] )
{
    time_t start, stop;
    time(&start);
    sleep( 5  ) ;
    time(&stop);
    printf("start : %ld\n",start );
    printf("stop  : %ld\n",stop );
    printf("in seconds: %f\n",difftime(stop,start));
}

Ce qui donne :

start : 1253277063
stop  : 1253277068
in seconds: 5.000000

6 Les commandes du pré-processeur

6.1 #, ##

manipulate strings

Exemple :

#define toxmlSart( tag  ) tag##_index++ ; file << "<" << #tag << " id='" << tag##_index <<"' >"  << std::endl ;

D'autres exemples :

#define toxmltag(  var ) file << "    <d id='" << #var << "' val='" << var << "'/>"   << std::endl ;
#define toxmlSart( tag  ) tag##_index++ ; file << "<" << #tag << " id='" << tag##_index <<"' >"  << std::endl ;
#define toxmlSartname( tag, name  ) tag##_index++ ; file << "<" << #tag << " id='" << name <<"' >"  << std::endl ;
#define toxmlSartE( tag ) file << "</" << #tag << ">"  << std::endl ;

6.2 traces écran

#include <fstream>
#ifdef UTILITIES_TRACE

    #define MYMSG_INFO " :" <<__FILE__ <<" ["<<__LINE__<<"]"
    #define MYTRACE_BEGIN std::cout <<
    #define MYTRACE_END  << MYMSG_INFO   

    #define mytr(  flow  ) MYTRACE_BEGIN flow MYTRACE_END
    #define mytrln( flow ) MYTRACE_BEGIN flow MYTRACE_END << std::endl 
    #define mytrBeginln( flow ) MYTRACE_BEGIN "Begin " << flow MYTRACE_END << "..." << std::endl 
    #define mytrEndln( flow ) MYTRACE_BEGIN "End " << flow MYTRACE_END << std::endl 

    #define myscrute(var)  mytr( #var << "=" << var )
    #define myscruteln(var)  mytrln( #var << "=" << var )
    
#else   
  
    #define mytr( flow )
    #define mytrln( flow )
    #define mytrBeginln( flow )
    #define mytrEndln( flow )
    #define myscrute(var)
    #define myscruteln(var)    

#endif

6.3 traces fichier

const char* tracePath = "/home/C07138/logs/gabv2/" ;
#define UTILITIES_TRACE
#include <libgen.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
const std::string currentDateTime() {
    time_t     now = time(0);
    struct tm  tstruct;
    char       buf[80];
    tstruct = *localtime(&now);
    strftime(buf, sizeof(buf), "%Y-%m-%d_%H-%M-%S", &tstruct);
    return buf;
}
const std::string currentDateTimeMs() {
    struct timeb tstructMs;
    ftime(&tstructMs);
    struct tm  tstruct;
    char buf[80];    
    tstruct = *localtime(&tstructMs.time);
    strftime(buf, sizeof(buf), "%Y-%m-%d_%H-%M-%S", &tstruct);
    std::stringstream ss (std::stringstream::in | std::stringstream::out);
    ss << buf << "-" << std::setfill('0') << std::setw(3) << tstructMs.millitm ;
    return ss.str() ;
}
class CAtTrace
{
private :
    static CAtTrace * _instance ;
    std::ofstream mytr_out ;        
    int argc ; 
    char ** argv ;
protected :    
    CAtTrace() ;
    void caclTraceFilePath();
public :
    pid_t pid ;    
    std::string hostname ; 
    std::string exename ; 
    std::string traceFilePath ; 
    static CAtTrace * instance() ;   
    void setExeName( int argc0, char ** argv0 );
    std::ofstream& open();
    std::ofstream& printBegin();
    void close();
};
#ifdef UTILITIES_TRACE    
    #define MYMSG_INFO  
    #define MYTRACE_BEGIN CAtTrace::instance()->open() <<"["<<__LINE__<<"] " <<  
    #define MYTRACE_END ; CAtTrace::instance()->close()
     
    #define mytr(  flow  ) MYTRACE_BEGIN flow MYTRACE_END
    #define mytrln( flow ) MYTRACE_BEGIN flow << std::endl  MYTRACE_END 
    #define mytrBeginln( flow ) MYTRACE_BEGIN "Begin " << flow  << "..." \
                                                        << std::endl MYTRACE_END
    #define mytrEndln( flow ) MYTRACE_BEGIN "End " << flow  \
                                                        << std::endl MYTRACE_END
    #define myscrute(var)  mytr( #var << "=" << var )
    #define myscruteln(var)  mytrln( #var << "=" << var )   
#else     
    #define mytr( flow )
    #define mytrln( flow )
    #define mytrBeginln( flow )
    #define mytrEndln( flow )
    #define myscrute(var)
    #define myscruteln(var)    
#endif
CAtTrace * CAtTrace::_instance = 0 ; 
CAtTrace * CAtTrace::instance()  
{
    if (_instance==NULL)
    {
        _instance= new CAtTrace() ;
    };
    return _instance ;
};
void CAtTrace::caclTraceFilePath()
{
    std::stringstream ss (std::stringstream::in | std::stringstream::out);
    ss << tracePath << hostname << "_" << exename << "_" << pid  << "_" \
                                                         << __FILE__ << ".log" ;    
    ss >> traceFilePath ;    
};
CAtTrace::CAtTrace()  
{
    exename = "NoExeNameSet" ;
    
    char name[ 256 ] ; 
    gethostname( name,  256 ) ;
    hostname = name ;
    pid = getpid();
    caclTraceFilePath() ;
};
std::ofstream& CAtTrace::printBegin()
{
    mytr_out.open( traceFilePath.c_str() , std::ios_base::app 
                                                        | std::ios_base::out );
    mytr_out << currentDateTimeMs() << " " << hostname << " " << exename << " "\
                                                    << pid << " " << __FILE__  ;
    return mytr_out;
}
void CAtTrace::setExeName( int argc0, char ** argv0 )
{
    argc = argc0 ;
    argv = argv0 ;
    exename = basename( argv[0] ) ;
    caclTraceFilePath();
    printBegin();
    mytr_out<<"["<<__LINE__<<"] " << "CAtTrace::setExeName " ;
    for (int argi = 1 ; argi < argc ; argi++ )
    {
        mytr_out << " arg[" << argi << "]=[ " << argv[argi] << " ]" ;
    }    
    mytr_out << std::endl ;
};
std::ofstream& CAtTrace::open()
{
    printBegin();
    return mytr_out;
};
void CAtTrace::close()
{
    mytr_out << std::flush ;
    mytr_out.close() ;
};
// ATTODO trace end 
//##############################################################################
int main( int argc, char *argv[] )
{
    CAtTrace::instance()->setExeName( argc, argv ) ;
    mytrln("La vie est belle 1 !");
    usleep( 5000 ) ;
    mytrln("La vie est belle 2 !");
    usleep( 4000 ) ;
    mytrln("La vie est belle 3 !");
    usleep( 1000 ) ;
    mytrln("La vie est belle 4 !");
    printf( "traceFilePath=%s\n" , CAtTrace::instance()->traceFilePath.c_str());
    printf( "more %s\n" , CAtTrace::instance()->traceFilePath.c_str() );
}

6.4 #define

define variables

6.5 #error

display an error message

6.6 #if, #ifdef, #ifndef, #else, #elif, #endif

conditional operators

6.7 #include

insert the contents of another file

6.8 #line

set line and file information

6.9 #pragma

implementation specific command

6.10 #undef

used to undefine variables

6.11 Predefined preprocessor variables

miscellaneous preprocessor variables

__LINE__
__FILE__
__DATE__
__TIME__
__cplusplus
__STDC__

6.12 Voir aussi

7 Opérateurs logique

§-1
&&
et logique
II
ou logique
!
non logique

8 Conversions

8.1 unicode, char, wchar_t

8.1.1 c2w

§-2
std::wstring & c2w(  const std::string & a, std::wstring & b ) 
{
	b = L"" ;
	for ( unsigned int i = 0; i < a.size(); i++)
	{
		b += a[i] ;
	}	
	return b ;
} ;

8.1.2 w2c

§-3
std::string & w2c(  const std::wstring & a, std::string & b ) 
{
	b = "" ;
	for ( unsigned int i = 0; i <a.size(); i++)
	{
		b += (char)a[i] ;
	}	
	return b ;
} ;

8.2 Texte vers scalaires

8.2.1 stringstream

§-4
// using stringstream constructors.
#include <iostream>
#include <sstream>
using namespace std;

int main () {

  int val;
  stringstream ss (stringstream::in | stringstream::out);

  ss << "120 42 377 6 5 2000";

  for (int n=0; n<6; n++)
  {
    ss >> val;
    cout << val*2 << endl;
  }

  return 0;
}

9 Exceptions

#include <iostream>
...
try
  {
    throw 20;
  }
  catch (int e)
  {
    std::cout << "An exception occurred. Exception Nr. " << e << std::endl;
  }


#include <exception>
...
class myError : public std::exception 
{
private :
    std::string _msg ;            
public :
    myError( const char * what0)   : exception( ), _msg( what0 ) 
    {
      _msg="myError : "+_msg ;
    }            
    ~myError( ) throw() 
    {}                
    virtual const char * what( ) const throw() 
    { 
      return _msg.c_str(); 
    } 
} ;
try 
{
  ...
}
catch (int param) { cout << "int exception"; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }

9.1 Try Finally

template <class PTR > 
    class TryFinallyPtr
    {
        public :
        	TryFinallyPtr( PTR * * ptr ) : _ptr( ptr )   { } 
            ~TryFinallyPtr(   )                         
            { 
            	if ( _ptr != NULL) 
            		{
            		delete *_ptr ;
            		*_ptr = NULL ;
            		_ptr = NULL ;
            		}
            }
        private :
            PTR * * _ptr ;            
    } ;

Exemple d'utilisation :

ParadXMLDriver_ns::CFileFormatInfo info ;
ParadXMLDriver_ns::CParadPlatform *  med ;

med = info.LoadFromXMLFile( fname ) ;
TryFinallyPtr< ParadXMLDriver_ns::CParadPlatform > TryFinallyMed(  &med ) ;
 ...

10 Lire un caractère depuis la console

10.1 _getch et _getche

10.1.1 Windows

Qu'une touche du clavier soit appuyée ou non _getch renvoie le code de la touche appuyée, n'attend pas l'appui sur une touche.

#include <conio.h>
...
int a = _getch() ;

10.1.2 Linux

Sous Linux, nous n'avons pas la commande équivalente, il faut un plus de code pour avoir l'équivalente.

10.1.2.1 Solution 1
#include <termios.h>
#include <unistd.h>

int mygetch(void)
{
struct termios oldt,
newt;
int ch;
tcgetattr( STDIN_FILENO, &oldt );
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
ch = getchar();
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
return ch;
}
10.1.2.2 Solution 2
/**
 Linux (POSIX) implementation of _kbhit().
 Morgan McGuire, morgan@cs.brown.edu
 */
#include <stdio.h>
#include <sys/select.h>
#include <termios.h>
#include <stropts.h>

int _kbhit() {
    static const int STDIN = 0;
    static bool initialized = false;

    if (! initialized) {
        // Use termios to turn off line buffering
        termios term;
        tcgetattr(STDIN, &term);
        term.c_lflag &= ~ICANON;
        tcsetattr(STDIN, TCSANOW, &term);
        setbuf(stdin, NULL);
        initialized = true;
    }

    int bytesWaiting;
    ioctl(STDIN, FIONREAD, &bytesWaiting);
    return bytesWaiting;
}

//////////////////////////////////////////////
//    Simple demo of _kbhit()

#include <unistd.h>

int main(int argc, char** argv) {
    printf("Press any key");
    while (! _kbhit()) {
        printf(".");
        fflush(stdout);
        usleep(1000);
    }
    printf("\nDone.\n");

    return 0;
}

10.2 getchar

getchar lit le code d'une touche de clavier appuyé, il attend, pour rendre la main, l'appui de la touche retour-chariot.

int a = _getch() ;

10.3 std::cin

std::cin permet aussi de lire un caractère au clavier, il attend, pour rendre la main, l'appui d'une touche autre que espace suivi l'appui de la touche retour-chariot.

#include <iostream>
...
char a ;
std::cin >> a ;

10.4 pause, wait_a_char

Voici la commande sui attend l'appui d'une touche au clavier sans demander l'appui sur return pour rendre la main (sous windows et linux).

#ifdef LINUX
#include <termios.h>
#include <unistd.h> 
int wait_a_char(void)
{
    wprintf( L"Press a key:\n"  ) ;
    struct termios oldt,
    newt;
    int ch;
    tcgetattr( STDIN_FILENO, &oldt );
    newt = oldt;
    newt.c_lflag &= ~( ICANON | ECHO );
    tcsetattr( STDIN_FILENO, TCSANOW, &newt );
    ch = getchar();
    tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
    return ch;
}
#endif

#ifdef WINDOWS
//#include <conio.h>
int wait_a_char()
{
	wprintf( L"Press a key:\n"  ) ;
	int a = _getch() ;
    return a
};
#endif

11 Pointeurs

11.1 Pointeurs de fonction

typedef void (*state_func)( wchar_t )   ;
...
void getA( wchar_t a )
{
} ;
state_func a = getA ;

11.2 Pointeurs de méthodes de classe

typedef  void (CParsor::*state_func)( wchar_t )   ;
class CParsor
{
protected :
	state_func process ;
public:
	CParsor() : process(NULL) 
	{
		process = &CParsor::zone_val ;
                (this->* process)( L'A' )  ;
	};
	void zone_val( wchar_t carac ) 
	{
	};
};

12 Template

12.1 Template de fonction - concept

template<typename Type>
Type minimum( Type a, Type b)
{
  return a < b ? a : b ;
}


template<typename T>
T minimum(T a, T b)
{
    return a < b ? a:b;
}

int f(int a, int b)
{
  return minimum(a,b) ;
}

float f(float a, float b)
{
  return minimum(a,b) ;
}


problème :

template<typename T1, typename T2>
T1 minimum(T1 a, T2 b)
{
    return a < b ? a:b;
}

auto x = minimum(3,9.2);

int f(int a, int b)
{
  return minimum(a,b) ;
}

float f(float a, float b)
{
  return minimum(a,b) ;
}
#include <iostream>

int main(){
    std::cout << minimum(3.5,10) << "\n" ;
    std::cout << minimum(10,3.5) << "\n" ;
}

donne ! :

3.5
3


Solution un peu olé olé :

template<typename T1, typename T2>
auto minimum(T1 a, T2 b) -> decltype( a<b ? a:b)
{
    return a < b ? a:b;
}

auto x = minimum(3,9.2);

int f(int a, int b)
{
  return minimum(a,b) ;
}

float f(float a, float b)
{
  return minimum(a,b) ;
}
#include <iostream>

int main(){
    std::cout << minimum(3.5,10) << "\n" ;
    std::cout << minimum(10,3.5) << "\n" ;
}
3.5
3.5


Solution c++ 14 :

template<typename T1, typename T2>
auto minimum(T1 a, T2 b) // c++ 14 optionnel -> decltype( a<b ? a:b)
{
    return a < b ? a:b;
}

auto x = minimum(3,9.2);

int f(int a, int b)
{
  return minimum(a,b) ;
}

float f(float a, float b)
{
  return minimum(a,b) ;
}
#include <iostream>

int main(){
    std::cout << minimum(3.5,10) << "\n" ;
    std::cout << minimum(10,3.5) << "\n" ;
}
3.5
3.5


A méditer :

template<typename T1, typename T2>
auto minimum(T1 a, T2 b) // c++ 14 optionnel -> decltype( a<b ? a:b)
{
    return a < b ? a:b;
}
auto x = minimum(3,9.2);
int f(int a, int b)
{
  return minimum(a,b) ;
}
float f(float a, float b)
{
  return minimum(a,b) ;
}
#include <iostream>
struct gadget {};
gadget minimum(gadget a, gadget b)
{
    return {} ;
}
int main(){
    gadget a,b ;
    gadget c= minimum(a,b) ;
    std::cout << minimum(6,3.5) << "\n" ;
}


la notion de concept :

// concept c++20
#include <iostream>
#include <concepts>
template<typename T1, typename T2>
concept comparable = requires(T1 a1, T2 a2)
{ { a1 < a2 };  };

template<typename T1, typename T2>
concept ordered = requires(T1 a1, T2 a2)
{ { a1.less_than(a2) } -> std::convertible_to<bool>;  };

template<typename T1, typename T2>
requires comparable<T1,T2>
auto minimum(T1 a, T2 b) 
{ return a < b ? a:b; }

template<typename T1, typename T2>
requires ordered<T1,T2>
auto minimum(T1 a , T2 b) 
{ return a.less_than(b) ? a : b; }

int f(int a, int b) { return minimum(a,b); }

float f(float a, float b) { return minimum(a,b); }

double f(double a, int b) { return minimum(a,b); }

struct gadget 
{
  bool less_than(gadget const&) { return true;}
};

int main(){
    gadget a,b ;
    gadget c = minimum( a, b)  ;
    std::cout << minimum(6,3.5) << "\n" ;
    std::cout << minimum(3.5,6) << "\n" ;
}


Retournant une classe :

template<class CCLASS> void fileToWideChar( std::string filename, CCLASS& callBackClass )
{
  ...
  callBackClass.takeWideChar( ... )
  ...
}

12.2 fonctions et classes amies dans un patron

class C {};
void f (int);
template <typename T> class A
{
public:
//...
friend class C;
friend void f (int);
};
A <int> xi;
A<std::string> xs;

12.3 patrons de fonctions et de classes amies dans une classe

template <typename U> class A{/*...*/};
template <typename T> class B
{
public:
//...
template <typename U> friend class A;
};
template <class U> class A{/*…*/};
template <class T> class B
{
public:
// only A<int> and A<string> are friends
friend class A <int>;
friend class A <std::string>;
};

13 Appel de commandes système

13.1 Récupérer le resultat dans un string

tdo-1

non testé

#include <string>
#include <iostream>
#include <stdio.h>

std::string exec(char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while(!feof(pipe)) {
    	if(fgets(buffer, 128, pipe) != NULL)
    		result += buffer;
    }
    pclose(pipe);
    return result;
}
Replace popen and pclose with _popen and _pclose for Windows.

14 Fichiers

14.1 chars_to_file

void chars_to_file( std::string filename, std::string text  )
{
	FILE *f ;
	f=fopen( filename.c_str(),"wb") ;
	//fwrite( text.c_str(), sizeof(char),text.size(), f ) ;
	for ( std::string::iterator c = text.begin(); c != text.end(); c++ )
	{
		putc( *c, f ) ;
	};
	fclose( f ) ;
} ;

14.2 file_to_chars

std::string & file_to_chars( std::string filename, std::string & text  )
{
	text = "" ;
	FILE *f ;
	f=fopen( filename.c_str(),"r") ;
	bool encore = true ;
	while( (! feof(f)) && (encore) )
	{
		char c = getc(f) ;
		encore = c != EOF ;
		if (encore)
		{
			text += c ;
		};
	} ;
	fclose( f ) ;
	return text ;
} ;

14.3 file_exists

#include <fstream>  // std::ifstream
bool file_exists (const std::string &filename)
{
    std::ifstream infile( filename.c_str() );
    return infile.good() ;
}

14.4 fileTxt_to_fileTxt_by_word

Al1
Attention16.png
Le fait d'utiliser des formats textes ne garantie pas la copie exacte des fichiers
#include <fstream>  // std::ifstream, std::ofstream
#include <string>   // std::string
void fileTxt_to_fileTxt_by_word( const char * filetxt1, const char * filetxt2   )
{
   std::ifstream ifs ( filetxt1 );
   std::ofstream ofs ( filetxt2 );
   if (ofs.good ())
   {
      std::string s;
      while (ifs >> s)
          {
              ofs << s << ' ';
          } ;
   }
}

14.5 fileTxt_to_fileTxt_by_line

Al2
Attention16.png
Le fichier destination aura toujours une saut de ligne en fin de fichier.
#include <fstream>   // std::ifstream, std::ofstream
#include <string>    // std::string, std::getline()
#include <iostream>  // std::endl
int fileTxt_to_fileTxt_by_line( const char * filetxt1, const char * filetxt2   )
{
   std::ifstream ifs ( filetxt1 );
   std::ofstream ofs ( filetxt2 ) ;
   std::string s;
   while ( std::getline (ifs, s) )
      ofs << s << std::endl ;
}

14.6 Lire un fichier texte ligne par ligne et colonne par colonne

//---------------------------------------------------------------------------
#include <iostream>
#include <fstream.h>
#include <strings.h>
#include <sstream.h>
// voir http://www.msoe.edu/eecs/ce/courseinfo/stl/string.htm
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
   std::ifstream fic("entree.txt");
   if ( ! fic.is_open( ) ) { std::cout << "Error opening file"; exit (1); }
   std::string line  ;
   while ( getline( fic , line , '\n') )
   {
     std::cout << line << std::endl ;
     std::stringstream sline( line ) ;
     std::string mot ;
     while ( ! sline.eof() )
       {
         sline >> mot ;
         std::cout << mot << std::endl ;
       } ;
   }
   return 0;
}
//---------------------------------------------------------------------------


tdo-2

Comment manipuler des fichiers

14.7 Liens

15 Héritage

15.1 Héritage non virtuelle

#include <iostream>

class classK
{
    
    public :
        int x ;
    classK() : x(0)
    {
        std::cout << "classK::classK()" << std::endl ;
    } ;
    virtual ~classK()
    {
        std::cout << "classK::~classK()" << std::endl ;
    } ;
    virtual void copy( classK * obj )
    {
        std::cout << "classK::copy( classK * obj )" << std::endl ;
        x = obj->x ;
    } ;
    virtual void clear()
    {
        std::cout << "classK::clear()" << std::endl ;
        x = 0 ;
    } ;
     
};

class classA : public classK
{
    
    public :
    classA() : classK()
    {
        std::cout << "classA::classA()" << std::endl ;
        x = 1 ;
    } ;
    ~classA()
    {
        std::cout << "classA::~classA()" << std::endl ;
    } ;
    void copy( classK * obj )
    {
        std::cout << "classA::copy( classK * obj )" << std::endl ;
        classK::copy( obj ) ;
    } ;
    void copy( classA * obj )
    {
        std::cout << "classA::copy( classA * obj )" << std::endl ;
        classK::copy( obj ) ;
    } ;
    
    void clear()
    {        
        std::cout << "classA::clear()" << std::endl ;
        classK::clear() ;
    } ;
     
};

class classB : public classK
{
    
    public :
    classB() : classK()
    {
        std::cout << "classB::classB()" << std::endl ;
        x = 2 ;
    } ;
    ~classB()
    {
        std::cout << "classB::~classB()" << std::endl ;
    } ;
    void copy( classK * obj )
    {        
        std::cout << "classB::copy( classK * obj )" << std::endl ;
        classK::copy( obj ) ;        
    } ;
    void copy( classB * obj )
    {        
        std::cout << "classB::copy( classB * obj )" << std::endl ;
        classK::copy( obj ) ;        
    } ;
    
    void clear()
    {
        std::cout << "classB::clear()" << std::endl ;
        classK::clear() ;
    } ;

    
    
};

class classC : public classA, classB
{
    /*
    La class C contient deux espaces mémoire distinctes pour l'ancetre classK ;
    classA::classK et classB::classK
     */
    
    public :
    classC() :  classA(), classB()
    {
        std::cout << "classC::classC()" << std::endl ;
    } ;
    ~classC()
    {
        std::cout << "classC::~classC()" << std::endl ;
    } ;
    
    void copy( classK * obj )
    {
        std::cout << "classC::copy( baseK* obj )" << std::endl ;        
        classA::copy( obj ) ;
        classB::copy( obj ) ;
        
    } ;

    void copy( classC * obj )
    {
        std::cout << "classC::copy( baseC* obj )" << std::endl ;        
        classA::copy( (classA * )obj ) ;
        classB::copy( (classB * )obj ) ;
        
    } ;
    
    
    void clear()
    {
        std::cout << "classC::clear()" << std::endl ;
        classA::clear() ;
        classB::clear() ;                        
        
    } ; 
    void print()
    {
        std::cout << "classA::x = " << classA::x << std::endl ;
        std::cout << "classB::x = " << classB::x << std::endl ;        
    } ;        
};

int main(int argc, char *argv[ ])
{
    classC * c = new classC() ;
    classC  c2 ;
    
    classA * a = (classA *) c ;
    classB * b = (classB *) c ;
    classK * ka = (classK *)(classA *) c ; 
    classK * kb = (classK *)(classB *) c ; 
    //classK * kk = (classK *) c ;  error: 'classK' is an ambiguous base of 'classC' 
    //classK * k = (classC *) c ; error: 'classK' is an ambiguous base of 'classC'     
    classK * ka2 = (classK* )(classA *) &c2 ; 
    
    c->print() ;
    c2.print() ;
    ka->clear() ;
    c->print() ;
                
    ka->copy( ka2 ) ;
    c->print() ;
    // pb car on appelle que le classK de classA => ka2, 
    //le polymorphisme de copy ne suffit pas pour une utilisation claire.   

    c->copy( &c2 ) ;
    c->print() ;
    // ici on maîtrise mieux la copie, mais on a perdu le polymorphisme.

    
    
    delete c ;
    return 0 ;
} ;
[atoross@claui2v9 /local01/atoross/dvlp/bin/attools/trunk/cpp]$./bin/heritage
classK::classK()
classA::classA()
classK::classK()
classB::classB()
classC::classC()
classK::classK()
classA::classA()
classK::classK()
classB::classB()
classC::classC()
classA::x = 1
classB::x = 2
classA::x = 1
classB::x = 2
classC::clear()
classA::clear()
classK::clear()
classB::clear()
classK::clear()
classA::x = 0
classB::x = 0
classC::copy( baseK* obj )
classA::copy( classK * obj )
classK::copy( classK * obj )
classB::copy( classK * obj )
classK::copy( classK * obj )
classA::x = 1
classB::x = 1
classC::copy( baseC* obj )
classA::copy( classA * obj )
classK::copy( classK * obj )
classB::copy( classB * obj )
classK::copy( classK * obj )
classA::x = 1
classB::x = 2
classC::~classC()
classB::~classB()
classK::~classK()
classA::~classA()
classK::~classK()
classC::~classC()
classB::~classB()
classK::~classK()
classA::~classA()
classK::~classK()

15.2 Héritage virtuel

#include <iostream>

class classK
{
    
    public :
        int x ;
    classK() : x(0)
    {
        std::cout << "classK::classK()" << std::endl ;
    } ;
    virtual ~classK()
    {
        std::cout << "classK::~classK()" << std::endl ;
    } ;
    virtual void copy( classK * obj )
    {
        std::cout << "classK::copy( classK * obj )" << std::endl ;
        x = obj->x ;
    } ;
    virtual void clear()
    {
        std::cout << "classK::clear()" << std::endl ;
        x = 0 ;
    } ;
     
};

class classA : public virtual classK
{
    
    public :
    classA() : classK()
    {
        std::cout << "classA::classA()" << std::endl ;
    } ;
    ~classA()
    {
        std::cout << "classA::~classA()" << std::endl ;
    } ;
    void copy( classK * obj )
    {
        std::cout << "classA::copy( classK * obj )" << std::endl ;
        classK::copy( obj ) ;
    } ;
    void copy( classA * obj )
    {
        std::cout << "classA::copy( classA * obj )" << std::endl ;
        classK::copy( obj ) ;
    } ;
    
    void clear()
    {        
        std::cout << "classA::clear()" << std::endl ;
        classK::clear() ;
    } ;
     
};

class classB : public virtual classK
{    
    public :
    classB() : classK()
    {
        std::cout << "classB::classB()" << std::endl ;
    } ;
    ~classB()
    {
        std::cout << "classB::~classB()" << std::endl ;
    } ;
    void copy( classK * obj )
    {        
        std::cout << "classB::copy( classK * obj )" << std::endl ;
        classK::copy( obj ) ;        
    } ;
    void copy( classB * obj )
    {        
        std::cout << "classB::copy( classB * obj )" << std::endl ;
        classK::copy( obj ) ;        
    } ;
    
    void clear()
    {
        std::cout << "classB::clear()" << std::endl ;
        classK::clear() ;
    } ;

    
    
};

class classC : public classA, classB
{
    /*
    La class C contient deux espaces mémoire distinctes pour l'ancetre classK ;
    classA::classK et classB::classK
     */
    
    public :
    classC() :  classA(), classB()
    {
        std::cout << "classC::classC()" << std::endl ;
    } ;
    
    ~classC()
    {
        std::cout << "classC::~classC()" << std::endl ;
    } ;
    
    void copy( classK * obj )
    {
        std::cout << "classC::copy( baseK* obj )" << std::endl ;        
        classA::copy( obj ) ;
        classB::copy( obj ) ; 
    } ;

    void copy( classC * obj )
    {
        std::cout << "classC::copy( baseC* obj )" << std::endl ;        
        classA::copy( (classA * )obj ) ;
        classB::copy( (classB * )obj ) ; 
    } ;
        
    void clear()
    {
        std::cout << "classC::clear()" << std::endl ;
        classA::clear() ;
        classB::clear() ;
        
    } ; 
    void print()
    {
        std::cout << "x = " << x << std::endl ;
    } ;        
};

int main(int argc, char *argv[ ])
{
    classC * c = new classC() ;
    classC  c2 ;
    
    classK * k = (classK *)c ; 
    
    c->print() ;
    c2.print() ;
    
    k->clear() ;
    c->print() ;
    // pb double clear de classK
    
    c2.x =3 ;
    k->copy( (classK*) &c2 ) ;
    c->print() ;
    // pb double copy de classK
    
    c2.x =4 ;
    c->copy( &c2 ) ;
    c->print() ;
    // pb double copy de classK
                    
    delete c ;
    return 0 ;
} ;
[atoross@claui2v9 /local01/atoross/dvlp/bin/attools/trunk/cpp]$./bin/heritage2
classK::classK()
classA::classA()
classB::classB()
classC::classC()
classK::classK()
classA::classA()
classB::classB()
classC::classC()
x = 0
x = 0
classC::clear()
classA::clear()
classK::clear()
classB::clear()
classK::clear()
x = 0
classC::copy( baseK* obj )
classA::copy( classK * obj )
classK::copy( classK * obj )
classB::copy( classK * obj )
classK::copy( classK * obj )
x = 3
classC::copy( baseC* obj )
classA::copy( classA * obj )
classK::copy( classK * obj )
classB::copy( classB * obj )
classK::copy( classK * obj )
x = 4
classC::~classC()
classB::~classB()
classA::~classA()
classK::~classK()
classC::~classC()
classB::~classB()
classA::~classA()
classK::~classK()


15.3 Utilisation de méthodes virtuelles, pure ou impure, dans les constructeurs et destructeurs de classes

Si l'on veut avoir un moyen d'intercepter la destruction d'une classe sans avoir à écrire le destructeur de la classe dérivée on peut faire appel à une méthode virtuelle pure, ici appelée onEnd.

Mais, hélas le code suivant ne se compile pas :

class A
{
public :
    A()
    {
        printf("A\n");
    } ;
    virtual ~A()
    {
        printf("~A\n");
        onEnd() ;
    } ;
    virtual void onEnd() = 0 ;
};

class B : public A
{
public :
    B()
    {
        printf("B\n");
    } ;
    ~B()
    {
        printf("~B\n");
    } ;
    void onEnd()
    {
        printf("B::onEnd\n");
    };
};

int main( int argc, char** argv )
{
    printf("Start\n");
    B b ;
    printf("End\n");
} ;

Le compilateur râle :

undefined reference to `A::onEnd()'

En cherchant sur le net je suis tombé sur :

  • Citation : Norme C++ (10.4)
Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined.

Le problème soulevé ici ce n'est pas le fait que le compilateur refuse de compiler, mais c'est que le compilateur accepte de compiler le code suivant :

#include <algorithm>
#include <vector>
#include <sstream>

class A
{
public :
    A()
    {
        printf("A\n");
    } ;
    virtual ~A()
    {
        printf("~A\n");
        onEnd() ;
    } ;
    virtual void onEnd()
    {
        printf("A::onEnd\n");
    } ;
};

class B : public A
{
public :
    B()
    {
        printf("B\n");
    } ;
    ~B()
    {
        printf("~B\n");
    } ;
    void onEnd()
    {
        printf("B::onEnd\n");
    };
};

int main( int argc, char** argv )
{
    printf("Start\n");
    B b ;
    printf("End\n");
} ;

En exécutant le programme on obtient :

Start
A
B
End
~B
~A
A::onEnd
Al3
Attention16.png
Nous voyons bien que la méthode virtuelle onEnd ne se comporte pas comme une méthode virtuelle dans le destructeur ! Si on ne fait pas attention, cela peut conduire à des bugs dans le programme.

Je ne comprends pas pourquoi ceci, car le compilateur peut détruire la table des méthodes virtuelles à la fin du destructeur. Dans d'autre langages tel que le pascal j'utilise des méthodes virtuelles dans les destructeurs.

tdo-3

Vérifier en pascal ce qui est dit ci-dessus.

Que faire alors ? Pour l'instant je n'ai trouvé que le moyen de ré-écrire le destructeur de la classe dérivée, on perd alors la sémantique donnée par le nom de la méthode onEnd ; ce qui signifie évènement arrivé avant la destructeur de la classe, ou alors il faut demander à l'utilisateur de l'API de rappeler onEnd dans le destructeur de sa classe.

Voici le code

#include <algorithm>
#include <vector>
#include <sstream>

class A
{
public :
    A()
    {
        printf("A\n");
    } ;
    virtual ~A()
    {
        printf("~A\n");
        onEnd() ;
    } ;
    virtual void onEnd()
    {
        printf("A::onEnd\n");
    } ;
};

class B : public A
{
public :
    B()
    {
        printf("B\n");
    } ;
    ~B()
    {
        printf("~B\n");
        onEnd() ;
    } ;
    void onEnd()
    {
        printf("B::onEnd\n");
    };
};

int main( int argc, char** argv )
{
    printf("Start\n");
    B b ;
    printf("End\n");
} ;

Programme exécuté donne :

Start
A
B
End
~B
B::onEnd
~A
A::onEnd

16 Singleton

Interface :

class CTestBase_factory
{
private :
    static CTestBase_factory * _instance ;
protected :    
    map_factory_t creators ;
    CTestBase_factory() ;
public :
    static CTestBase_factory * instance() ;     
    CTestBase * create( const char * classname ) ;
    void register_creator( const char * classname, create_func_t create_func ) ;
};

Implémentation :

CTestBase_factory * CTestBase_factory::_instance = 0 ;

CTestBase_factory * CTestBase_factory::instance()  
{
    if (_instance==NULL)
    {
        _instance= new CTestBase_factory() ;
    };
    return _instance ;
};

CTestBase_factory::CTestBase_factory() : creators() 
{
	register_creator( "CTest", CTest_factory ) ;  
        ... 
};

CTestBase * CTestBase_factory::create( const char * classname )
{
    return creators[ classname ]() ;    
};

void CTestBase_factory::register_creator( const char * classname, create_func_t create_func ) 
{
    creators[ classname ] = create_func ;
};


17 Surchage d'opérateurs

17.1 opérateur []

#include <iostream>
class classA
{
protected :
    float rayon ;
    float nan ;
public :
    classA() : nan(0.0), rayon( 1.2 ) {} ;
    ~classA() {} ;
};
class classB : public classA
{
public :
    classB() : classA() {} ;
    ~classB() {} ;
    float & operator []( int index )
    {
        if (index==1) { return rayon ; }
        else { return nan ; }
    }
};
int main(int argc, char *argv[ ])
{
    classB  b ;
    for (int i = 0;  i< 10 ; i++) { std::cout << b[ i ] << std::endl ; }
    b[ 1 ] = 5.6 ;
    b[ 5 ] = 8.5 ;
    for (int i = 0;  i< 10 ; i++) { std::cout << b[ i ] << std::endl ; }
    return 0 ;
} ;
Rq La valeur de retour comme référence float & permet de l'assignement b[ 1 ] = 5.6


Ce qui donne :

0
1.2
0
0
0
0
0
0
0
0
8.5
5.6
8.5
8.5
8.5
8.5
8.5
8.5
8.5
8.5

18 typecasting

18.1 RTTI : typeid

Exemple simple :

#include <typeinfo>
...
int X ;
std::cout <<  "type de X " << typeid( X ).name() << std::endl ;
...
int *X ;
std::cout <<  "type de X " << typeid( X ).name() << std::endl ;
std::cout <<  "type de X " << typeid( *X ).name() << std::endl ;
...

Exemple plus complet :

// typeid, polymorphic class
#include <iostream>
#include <typeinfo>
#include <exception>
using namespace std;

class CBase { virtual void f(){} };
class CDerived : public CBase {};

int main () {
  try {
    CBase* a = new CBase;
    CBase* b = new CDerived;
    cout << "a is: " << typeid(a).name() << '\n';
    cout << "b is: " << typeid(b).name() << '\n';
    cout << "*a is: " << typeid(*a).name() << '\n';
    cout << "*b is: " << typeid(*b).name() << '\n';
  } catch (exception& e) { cout << "Exception: " << e.what() << endl; }
  return 0;
}

donne :

a is: class CBase *
b is: class CBase *
*a is: class CBase
*b is: class CDerived

18.2 Voir aussi

19 STL

19.1 vector, les vecteurs ou tableaux

19.1.1 Parcourir les vecteurs

#include <iostream>
#include <vector>
using namespace std ;
int main()
{
    vector< size_t > V(5) ;
    V[4] = 4 ;
    V[3] = 3 ;
    V[2] = 2 ;
    V[1] = 1 ;
    V[0] = 0 ;
    vector< size_t >::iterator v = V.end() ;
    v-- ;
    while (v >= V.begin() )
    {
        cout << *v << endl ;
        v-- ;
    };
    cout << "--" << endl ;
    v = V.begin();
    while (v < V.end() )
    {
        cout << *v << endl ;
        v++ ;
    };

    return 0 ;
}

19.2 map, les dictionnaires

std::map<const char*, int > a ;
a["toto"] = 1 ;
a["titi"] = 2 ;
std::cout << a["toto"] << std::endl ;
std::cout << a["toti"] << std::endl ;

donne

1
0
Rq Si la clé n'est pas trouvé, la valeur par défaut du type est renvoyée.

20 Avec des sources UTF8

20.1 avec utf8-cpp

#include <iostream>
#include <string>
#include <clocale>
#include <utf8.h>

int main() {
    char* local = setlocale(LC_CTYPE,"");
    std::cout << "local : " << local << std::endl ;
    std::string txt = "Cet été je serai en vacances. Le nombre π vaut 3.14. Voici une phrase écrite en arménien; «Կյանքը գեղեցիկ Ե»" ;
    std::cout << "txt  : " << txt << std::endl ;

    std::wstring wtxt ;
    utf8::utf8to16( txt.begin(), txt.end(), back_inserter(wtxt) )  ;

    std::string txtc ;
    utf8::utf16to8( wtxt.begin(), wtxt.end(), back_inserter(txtc) )  ;
    std::cout << "txtc : " << txtc << std::endl ;
    return 0;
};

ce qui produit :

local : fr_FR.UTF-8
txt  : Cet été je serai en vacances. Le nombre π vaut 3.14. Voici une phrase écrite en arménien; «Կյանքը գեղեցիկ Ե»
txtc : Cet été je serai en vacances. Le nombre π vaut 3.14. Voici une phrase écrite en arménien; «Կյանքը գեղեցիկ Ե»

20.2 Avec c++11

20.3 Conversion d'un « string »

#include <cstdlib>
#include <iostream>
#include <locale.h>
#include <string>
#include <locale>
#include <codecvt>
#include <cassert>

int main() {

    const auto str = u8"Cet été je serai en vacances. Le nombre π vaut 3.14. Voici une phrase écrite en arménien; «Կյանքը գեղեցիկ Ե»";
    wstring_convert<codecvt_utf8<char32_t>, char32_t> cv;
    auto str32 = cv.from_bytes(str);
    for (auto c : str32)
        cout << uint_least32_t(c) << '\n';

    return 0;
}

20.4 Conversion d'un « fichier »

#include<iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <cstdint>
#include <locale>
#include <codecvt>
using namespace std;

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch (std::range_error& e)
    {
        size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for (size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}
int main()
{
    // read entire file into string
    if (std::ifstream is{ "C:\\Users\\hsingh\\Documents\\Visual Studio 2017\\Projects\\ConsoleApplication4\\Debug\\test.txt", std::ios::binary

21 Expressions régulières

#include <iostream>
#include <string>
#include <regex>

int main()
{
   // Regular Expression TR1 Example
   
   // Create a string - This is what we will search
   const std::string SearchString("Hello World, My email is tom@lolfake.com");

   // Let's grab the email address
   const std::tr1::regex RegPtnEmail("([\\w-+]+(?:\\.[\\w-+]+)*@(?:[\\w-]+\\.)+[a-zA-Z]{2,7})");

   std::tr1::smatch Result;

   if (std::tr1::regex_search(SearchString, Result, RegPtnEmail))
      std::cout << Result[1] << std::endl;
   else
      std::cerr << "No Email found" << std::endl;

   // Let's try a different email
   const std::string NewEmail("asdasdasdasdasd lol@gmail.com");

   if (std::tr1::regex_search(NewEmail, Result, RegPtnEmail))
      std::cout << Result[1] << std::endl;
   else
      std::cerr << "No Email found" << std::endl;

   // Let's try a unfinished email
   const std::string FakeEmail("Hello - - lol@gmail");

   if (std::tr1::regex_search(FakeEmail, Result, RegPtnEmail))
      std::cout << Result[1] << std::endl;
   else
      std::cerr << "No Email found" << std::endl;

   return 0;
}