IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

question sur l'opérateur new


Sujet :

C++

  1. #1
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut question sur l'opérateur new
    Bonjour

    J'ai une question sur l'opérateur new.
    Quand je lance le programme suivant, la valeur par défaut de l'opérateur size est 1.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    class X {
    public:
         static void * operator new(size_t size);
    	 static void * operator new(size_t, new_handler ) ; 
    };
     
    void * X::operator new(size_t size, new_handler p){
       set_new_handler(p);
       return ::operator new(sizeof(X)*size); 
    }
     
    void* X::operator new(size_t size){
    		  return ::operator new(sizeof(X)*size);   
    	  };
     
     
    void specialErrorHandler(){
    	cerr<<"Erreur!!!";//il vaut mieux utiliser cerr+abort() que cout
    	abort();
    }
     
     
    int main(){
     
    X *px1 =new(specialErrorHandler) X; // calls X::operator new
     
    X *px2 = new X; // error!
    }
    Je voudrais déjà savoir si c'est possible de modifier la valeur par défaut pour ne plus qu'elle soit fixée à 1? Par exemple:
    X *px2=new(2) X, mais le compilateur refuse un tel appel.


    Plus généralement,
    quelle est la différence entre:
    et
    Je vous remercie

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    oula.. c'est juste pas la même chose.

    C'est même pas les mêmes opérateurs.

    Le premier alloue un entier et lui affecte 3 et o pointera sur cet entier.
    le second alloue deux entiers (un tableau) default-initialized et o pointera sur le premier élément du tableau.

  3. #3
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Citation Envoyé par Goten
    C'est même pas les mêmes opérateurs.
    +1
    Le premier c'est l'opérateur new.
    Le deuxième c'est l'opérateur new[].

  4. #4
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    ok,merci.
    et pour ma premier question (celle avec size_t?)

  5. #5
    Modérateur
    Avatar de bruno_pages
    Homme Profil pro
    ingénieur informaticien à la retraite
    Inscrit en
    Juin 2005
    Messages
    3 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur informaticien à la retraite
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2005
    Messages : 3 534
    Points : 6 723
    Points
    6 723
    Par défaut
    Citation Envoyé par deubelte Voir le message
    ok,merci.
    et pour ma premier question (celle avec size_t?)
    mais vous y avez répondu par vous même, il faut utiliser new[], ou alors je n'ai pas compris la question

  6. #6
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    size représente la taille de la classe (son sizeof)

    Dans ton exemple tu lui passe la classe X qui est vide, et sa taille vaut donc 1. (j'aurais misé sur zéro mais bon )

    Si tu rajoutes une variable membre de type int à X alors size vaudra 4.

    new a besoin de cette info pour allouer la mémoire nécessaire pour contenir l'instance de l'objet. En simplifié new c'est à peu près équivalent à faire un malloc(sizeof(X)) + appeler le constructeur de X.

  7. #7
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    En fait, je viens de comprendre que j'avais répondu moi-même à la question.
    l'opérateur définit dans ma classe n'alloue qu'un et un seul pointeur.

    Donc, ok j'ai compris. Ensuite, j'ai effectivement essayer l'opérateur new[]:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    static void * operator new[](size_t size){
    		  return ::operator new[sizeof(X)*size]; //remarquons ici les :: pour la portée globale. 
    	  };
    mais le compilateur me renvoie comme erreur:

    "cannot determine which instance of overloaded function operator new is intended".

  8. #8
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Ben oui, ça devrait être
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    static void * operator new[](size_t size)
    {
      return ::operator new[](size);
    };
    Pour l'opérateur new[] le paramètre size représente la taille de la classe X multipliée par le nombre d'élément dans le tableau. Si X contient un unique int alors :
    appellera l'opérateur new[] avec un size valant 8.
    C'est à peu près équivalent à faire un malloc(sizeof(X) * 2) + 2 appels au constructeur de X pour les deux cases du tableau.

  9. #9
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    j'aurais misé sur zéro mais bon
    une instance de classe (même d'une classe vide) ne vaut jamais zero. En effet, un objet étant un espace, que serait un objet dont l'espace est nul?
    Donc tout objet, par défaut, vaut au moins 1 octet.
    (d'après Scott Meyers)

  10. #10
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    dernière chose:
    êtes vous d'accord pour dire que les deux fonctions delete correspondantes sont celles-ci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    static void operator delete(void *deadObject){
       ::operator delete(deadObject);
    };
     
    static void operator delete[](void *deadObject){
       ::operator delete[](deadObject);
    };

  11. #11
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Salut,
    Citation Envoyé par deubelte Voir le message
    une instance de classe (même d'une classe vide) ne vaut jamais zero. En effet, un objet étant un espace, que serait un objet dont l'espace est nul?
    Donc tout objet, par défaut, vaut au moins 1 octet.
    (d'après Scott Meyers)
    Heuu, oui, effectivement, mais pas forcément...

    D'abord, il vaut mieux parler de byte plutôt que d'octet, parce que le terme "octet" donne la fausse idée qu'il s'agira d'office... d'un groupement de 8 bits alors que ce n'est pas forcément le cas.

    Si tu tiens à franciser le terme byte, il faudrait plutôt parler de "multiplet"

    Pour rappel, un byte est "un espace de valeurs capable de représenter au minimum l'ensemble des caractères utilisés...

    Pour l'instant, étant donné que l'on travaille encore principalement avec les tables de caractères telles que ASCII étendues ou OCBD, cela correspond effectivement à 8 bits, mais certains systèmes utilisent (encore) la table ASCII non étendue et rien n'empêcherait un éditeur de compilateur / de système d'exploitation / fondeur de processeur de décider d'utiliser les tables ISO 8859 comme base, qui sont, me semble-t-il codées sur 16 bits

    Mais bon, ca, ce n'est encore qu'un détail.

    Par contre, une classe vide en tant que telle n'a que très peu d'intérêt, sauf si elle intervient dans un processus d'héritage ou de traits et de politiques.

    Et, dans ce cas, les compilateurs sont généralement capables d'appliquer une "empty base class optimization" (une optimisation des classes de base vides) qui aura pour résultat d'éviter justement de rajouter un byte issu d'une classe de base vide aux classes qui en dérivent.

    Il n'y aura donc que si tu essaye effectivement de créer une instance de la classe de base que tu te retrouvera dans cette situation d'avoir une classe vide qui fait... un byte

  12. #12
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    Toujours pour continuer sur les new:
    quelle est la différence entre ces trois déclarations:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    	int *o1=new int;
    	int *o2=new(int); 
    	int *o3(new int);
    pour moi, elles sont toutes les trois équivalentes.

  13. #13
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Oui, elles sont équivalentes.

    Par contre, il y a une sorte de convention tacite dans la communauté C++ pour n'employer que la forme 1.
    Je n'ai jamais vu la forme 2 et 3 utilisée en pratique.

    Edit : Pff, comme d'hab en C++ il y a toujours une exception à la règle.
    Je viens de me souvenir d'un cas où la forme 3 est obligatoire : les shared_ptr.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    boost::shared_ptr<int> sp = new int; // plante à la compilation;
    boost::shared_ptr<int> sp(new int); // OK

  14. #14
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    personnellement, je trouve que la 2 est plus explicite, car après tout new est une fonction, donc elle prend des arguments.

    d'ailleurs, on voit que l'argument est int. or, dans la fonction new que j'ai envoyée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       static void * operator new(size_t size);
    l'argument est un nombre.

    J'ai testé avec int, comme avec d'autres types de données. Il y a donc une conversion implicite qui se passe. Si on envoie int, size_t sera égale à 4,
    si on envoie une classe comprenant 2 int, size_t vaudra 8....
    mais je voudrais savoir comment se passe la conversion implicite. Je veux dire par là: comment le compilateur fait il pour connaitre la taille?

  15. #15
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 629
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Attention, cela a beau ressembler à une fonction, new n'est pas une "simple" fonction, dans le sens où c'est un opérateur, ainsi que l'indique très clairement le mot clé operator dans sa signature...

    C'est donc beaucoup plus proche des opérateurs +, -, * ou = que des fonctions "simples" nécessitant une paire de parenthèses et une liste d'arguments

    Or, dans le cas des opérateurs, l'utilisation des parenthèses est limitée à la modification des priorités des différents opérateurs

  16. #16
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    Attention, cela a beau ressembler à une fonction, new n'est pas une "simple" fonction, dans le sens où c'est un opérateur, ainsi que l'indique très clairement le mot clé operator dans sa signature...
    les opérateurs sont pas des fonctions?


    je viens aussi de voir que le type renvoyé par new est un pointeur sur void. Ce qui est logique puisque l'opérateur new créer une place mémoire. Mais ensuite, il faut convertir cette place mémoire pour que cela puisse correspondre avec ce que l'on veut stocké.

    int *o=new(int);

    ici, c'est typé. new(int) renvoie un pointeur sur void. Mais en toute rigueur, ne faudrait il pas écrire:
    int *o=static_cast<int>(new(int))?

    merci

  17. #17
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Je viens de me souvenir d'un cas où la forme 3 est obligatoire : les shared_ptr.
    ? C'est normal, constructeur tout ça.

    De toute façon : Utilise make_shared .

  18. #18
    Modérateur
    Avatar de bruno_pages
    Homme Profil pro
    ingénieur informaticien à la retraite
    Inscrit en
    Juin 2005
    Messages
    3 534
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : ingénieur informaticien à la retraite
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Juin 2005
    Messages : 3 534
    Points : 6 723
    Points
    6 723
    Par défaut
    Citation Envoyé par deubelte Voir le message
    les opérateurs sont pas des fonctions?
    et non

    ...ici, c'est typé. new(int) renvoie un pointeur sur void. Mais en toute rigueur, ne faudrait il pas écrire:
    int *o=static_cast<int>(new(int))?
    non car là il est clair que le but est de renvoyé un int *, ce qui n'est pas le cas lorsqu'il n'y a pas de contexte comme au début de la discussion. Le compilateur n'est pas sadique

  19. #19
    Rédacteur
    Avatar de Laurent Gomila
    Profil pro
    Développeur informatique
    Inscrit en
    Avril 2003
    Messages
    10 651
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Avril 2003
    Messages : 10 651
    Points : 15 920
    Points
    15 920
    Par défaut
    je viens aussi de voir que le type renvoyé par new est un pointeur sur void. Ce qui est logique puisque l'opérateur new créer une place mémoire. Mais ensuite, il faut convertir cette place mémoire pour que cela puisse correspondre avec ce que l'on veut stocké.

    int *o=new(int);

    ici, c'est typé. new(int) renvoie un pointeur sur void. Mais en toute rigueur, ne faudrait il pas écrire:
    int *o=static_cast<int>(new(int))?
    Le mot-clé new n'est pas un appel direct à la fonction operator new. Le compilo fait sa petite mixture interne pour le remplacer par un appel à operator new avec la taille déduite automatiquement, puis une construction de l'objet à l'espace mémoire renvoyé, et enfin renvoi d'un T* et non d'un void*.

  20. #20
    Débutant  
    Inscrit en
    Novembre 2006
    Messages
    1 073
    Détails du profil
    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 073
    Points : 217
    Points
    217
    Par défaut
    J'ai une question, sur l'opérateur delete cette fois-ci, mais qui concerne aussi l'allocation.
    J'ai crée cette classe String:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    class String{
    public:
        String(const char *value);
        ~String();
        operator const char *() const;
        void setter(char*);
    private:
        char *data;};
        void String::setter(char* donnes){
            *data=*donnes;}
     
        inline  String::operator const char*() const {
            return data;
        }
     
     
        String::String(const char *value){
            if (value) {
                data = new char[strlen(value) + 1];
                strcpy(data, value);}
            else {
                data = new char[1];
                *data = '\0';}
        }
     
        inline String::~String() {delete [] data;}
    Ensuite, j'ai crée cette fonction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    String f2(){
    	String p=String("ee");
        return p;
    }
    Maintenant, je crée ce main:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(){
    const char* pc=f2();
     
    cout<<pc;

    Bien sur, cela ne fonctionne pas, car quand p est crée dans la fonction, celui-ci est nécessairement détruit quand on sort, par le destructeur qui détruit la donnée membre.
    Le problème est que je voudrais renvoyer un objet String. Je veux aussi consrver le destructeur.
    Alors c'est un peu le serpent qui se mord la queue. Quelle pourrait être une solution? Un renvoie par référence ou par pointeur nefonctionne pas non plus, puisque l'objet qui est pointé (ou dont la référence est le nom) est détruit...
    merci

Discussions similaires

  1. Question sur le mot new
    Par heeedi dans le forum Débuter avec Java
    Réponses: 5
    Dernier message: 03/08/2009, 11h20
  2. Question sur l'opérateur ::
    Par johnkro dans le forum Langage
    Réponses: 8
    Dernier message: 14/03/2008, 10h13
  3. Questions sur l'opérateur <<
    Par coyotte507 dans le forum C++
    Réponses: 4
    Dernier message: 18/11/2007, 22h54
  4. Question sur String variable = new String( )
    Par beegees dans le forum Langage
    Réponses: 12
    Dernier message: 14/05/2007, 21h38
  5. question sur les opérateurs
    Par isidore dans le forum C++
    Réponses: 10
    Dernier message: 25/02/2005, 18h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo