Constructori multipli pentru clase in Cpp

Stau acum si fac niste modificari / optimizari intr-un proiect, scris, de sigur, de altcineva. Si, surpriza, ca de fiecare data cand faci review ( caci asta faci inainte de a te apuca sa modifici jumate din codul care e acolo ) gasesti ceva care nu e in regula si care te deranjeaza… ceva atat de mic… dar…

Deci, sa trec la tema. Dupa cum se stie in Cpp se pot defini mai multi constructori, astfel, un exemplu:

class Cluster {
private:
  int a;
  int b;
  int c;
  int d;  //aceasta clasa contine foarte multe variabile interne
public:
  //constructorul creeaza clasa pornind de la alta clasa
  Cluster( CAnotherClass *_class );
  // ctor de copiere
  Cluster( Cluster *_cluster );
  // ctor default
  Cluster( );
};

La prima vedere, acest cod pare nevinovat, dar daca te uiti in implementarea constructorilor, ar arata cam astfel :

Cluster::Cluster( CAnotherClass *_c ) : a( _c->a )
    , b( _c->b )
    , c( _c->c )
    , d( _c ->d )
{ /* no code */ }
Cluster::Cluster( Cluster *_c ) : a( _c->a )
    , b( _c->b )
    , c( _c->c )
    , d( _c ->d )
{ /* no code */ }
Cluster::Cluster( ) : a( 0 )
    , b( 0 )
    , c( 0 )
    , d( 0 )
{ /* no code */ }

in fine, mai pui si constructorul de copiere… si ai 100 de linii de cod cu constructori doar.

Partea nasoala apare doar atunci cand vrei sa adaugi / scoti un membru nou din clasa… ceea ce inseamna ca trebui sa modifici toti constructorii… Poate nu e o problema modificarea constructorilor, dar ce faci daca uiti sa faci modificarile intr-un constructor ( daca ai multe surse se poate intimpla foarte des ).

Pentru a face putin ordine in toti constructorii se procedeaza astfel :
1. se alege o functie ( doar una, posibil privata ) care va avea rolul de a atribui valori membrilor privati

Cluster::set( int a, int b, int c, int d );

2. celalti constructori apeleaza doar functia de set definit la punctul 1.

Cluster::Cluster( CAnotherClass *_c )
{
   set( _c->a, _c->b, _c->c, _c->d );
}
Cluster::Cluster( Cluster *_c )
{
   set( _c->a, _c->b, _c->c, _c->d );
}
Cluster::Cluster( )
{
   set( 0, 0, 0, 0 );
}

Prin o astfel de abordare a problemei aveti doar de castigat din urmatoarele motive:

1. atunci cand adaugi un membru nou in clasa, modifici functia set, iar functiile care folosesc functia set o sa dea eroare la compilare ( compilatorul aici e un ajutor pentru tine, iar in celalalt caz trebuie sa modifici manual in fiecare constructor cu posibilitatea ca poti sa omiti undeva modificarile, compilatorul nu iti da nici o eroare, iar la rulare se poate apela un constructor care nu l-ai update-at si care ti-a lasat noua variabila neinitializata ).

2. daca la un moment dat iti dai seama ca o variabila trebuie tratata mai speciala, sa ii faci nishte prelucrari inainte, atunci acest cod nou o sa il scrii doar in functia de set, nu o sa duplici mai apoi codul in toti constructorii.

3. clasa este mult mai bine structurata si ofera o logica celui care o sa ii face review mai tarziu.

cred ca atat.

[c++] fopen tip

Atunci cand se aglomereaza mai multe buguri…. se produce o crapaciune ( crash :) ), in rest exista doar noroc.

Acum, am dat de un bug foarte subtil ( care, la mine, se reproduce in combinatie cu alt bug :) ).

Sa exemplific prin cod:

  char *filename = "test.txt";
  FILE* file = fopen( filename, "w" );
  fclose( file );
  /* some code */

Ideea e ca functia fopen poate esua si astfel variabila “file” se seteaza cu NULL si in continuare pasand variabila “file” cu valoare NULL functiei fclose duce la o crapaciune foarte urata… ceea ce ar fi bine de evitat

Corect ar fi :

  char *filename = "test.txt";
  FILE* file = fopen( filename, "w" );
  if ( file )
    fclose( file );
  /* some code */

enjoy.

c++ creare matrice

#include <stdio.h>

typedef int matrice[10][10];

#define LINII 4
#define COLOANE 3

int **invers( matrice source, int n, int m )
{
    // acest pointer va salva matricea
    int **tmp = NULL;
    // definesti un tip nou de date (int*) caci nu poti sa
    //apelezi direct "new (int*)[]"
    typedef int* pInt;
    // aici creezi un vector unde vei salva pointerii pentru
    //fiecare linie
    tmp = new pInt[ n ];
    // pentru fiecare linie... creezi zona pentru date...
    //si salvezi pointeru in tmp[i]
    for ( int i=0; i<n; i++ )
        tmp[ i ] = new int[ m ];

    // inversarea fiecarei linii
    for ( int i=0; i<n; i++ )
        for ( int j=0; j<m; j++ )
            tmp[i][j] = source[i][m-1-j];

    // returnezi pointeru
    return tmp;
}

int main()
{
    /* initiliazez prima matrice */
    matrice a = { { 1,2,3 },
            { 4,5,6 }, { 7,8,9 }, {10,11,12} };
    int **b;

    /* functia ce creaza o matrice */
    b = invers( a, LINII, COLOANE );

    /* afisez matricea */
    for ( int i=0; i<LINII; i++ ) {
        for ( int j=0; j<COLOANE; j++ )
            printf( "%4d ", b[i][j] );
        printf( "\n" );
    }

    /* sterg matricea b din memorie */
    for ( int i=0; i<LINII; i++ )
        //sterg fiecare linie
        delete [](b[i]);
   
    //sterg vectoru care memora pointerii
    //catre vectorii cu continutu liniilor
    delete []b;

    return 0;
}

atat