web-dev-qa-db-fra.com

Est-il possible de changer la classe d'un objet C ++ après instanciation?

J'ai un tas de classes qui héritent toutes des mêmes attributs d'une classe de base commune. La classe de base implémente certaines fonctions virtuelles qui fonctionnent dans des cas généraux, tandis que chaque sous-classe réimplémente ces fonctions virtuelles pour une variété de cas spéciaux.

Voici la situation: je veux que la spécificité de ces objets sous-classés soit consommable. Essentiellement, je voudrais implémenter une fonction expend() qui fait perdre à un objet son identité de sous-classe et redevient une instance de classe de base avec les comportements de cas général implémentés dans la classe de base.

Je dois noter que les classes dérivées n'introduisent aucune variable supplémentaire, donc les classes de base et dérivées doivent avoir la même taille en mémoire.

Je suis prêt à détruire l'ancien objet et à en créer un nouveau, tant que je peux créer le nouvel objet à la même adresse mémoire, afin que les pointeurs existants ne soient pas cassés.

La tentative suivante ne fonctionne pas et produit un comportement apparemment inattendu. Qu'est-ce que j'oublie ici?

#include <iostream>

class Base {
public:
    virtual void whoami() { 
        std::cout << "I am Base\n"; 
    }
};

class Derived : public Base {
public:
    void whoami() {
        std::cout << "I am Derived\n";
    }
};

Base* object;

int main() {
    object = new Derived; //assign a new Derived class instance
    object->whoami(); //this prints "I am Derived"

    Base baseObject;
    *object = baseObject; //reassign existing object to a different type
    object->whoami(); //but it *STILL* prints "I am Derived" (!)

    return 0;
}
41
dwk

Vous pouvez au détriment des bonnes pratiques et maintenir le code dangereux. D'autres réponses vous fourniront des astuces désagréables pour y parvenir.

Je n'aime pas les réponses qui disent simplement "vous ne devriez pas faire ça", mais je voudrais suggérer qu'il y a probablement une meilleure façon d'atteindre le résultat que vous recherchez.

Le modèle de stratégie comme suggéré dans un commentaire de @ manni66 est bon.

Vous devriez également penser à conception orientée données , car une hiérarchie de classes ne semble pas être un choix judicieux dans votre cas.

35
Jean-Bernard Jansen

Oui et non. Une classe C++ définit le type d'une région mémoire qui est un objet. Une fois la région de mémoire instanciée, son type est défini. Vous pouvez essayer de travailler autour le système de type bien sûr, mais le compilateur ne vous laissera pas vous en tirer. Tôt ou tard, cela vous tirera dans le pied, car le compilateur a fait une hypothèse sur les types que vous avez violés, et il n'y a aucun moyen d'empêcher le compilateur de faire une telle hypothèse de manière portable.

Cependant, il existe un modèle de conception pour cela: c'est "State". Vous extrayez ce qui change dans sa propre hiérarchie de classes, avec sa propre classe de base, et vos objets stockent un pointeur vers la base d'état abstrait de cette nouvelle hiérarchie. Vous pouvez ensuite les échanger au contenu de votre cœur.

16
StoryTeller

Non, il n'est pas possible de changer le type d'un objet une fois instancié.

*object = baseObject; ne change pas le type de object, il appelle simplement un opérateur d'affectation généré par le compilateur.

Cela aurait été différent si vous aviez écrit

object = new Base;

(n'oubliez pas d'appeler delete naturellement; actuellement votre code laisse fuir un objet).

C++ 11 en avant vous donne la possibilité de déplacer les ressources d'un objet à un autre; voir

http://en.cppreference.com/w/cpp/utility/move

14
Bathsheba

Je suis prêt à détruire l'ancien objet et à en créer un nouveau, tant que je peux créer le nouvel objet à la même adresse mémoire, afin que les pointeurs existants ne soient pas cassés.

Le standard C++ aborde explicitement cette idée dans la section 3.8 (durée de vie de l'objet):

Si , après la fin de la durée de vie d'un objet et avant la réutilisation ou la libération du stockage occupé par l'objet, un nouvel objet est créé à l'emplacement de stockage qu'occupait l'objet d'origine, un pointeur qui pointait vers l'objet d'origine , une référence qui faisait référence à l'objet d'origine ou le nom de l'objet d'origine se référer automatiquement au nouvel objet et, une fois la durée de vie du nouvel objet commencée, peut être utilisé pour manipuler le nouvel objet <snip>

Oh wow, c'est exactement ce que tu voulais. Mais je n'ai pas montré toute la règle. Voici le reste:

si :

  • le stockage du nouvel objet recouvre exactement l'emplacement de stockage qu'occupait l'objet d'origine, et
  • le nouvel objet est du même type que l'objet d'origine (en ignorant les qualificatifs cv de niveau supérieur) , et
  • le type de l'objet d'origine n'est pas qualifié par const et, s'il s'agit d'un type de classe, ne contient aucun membre de données non statique dont le type est qualifié par const ou un type de référence, et
  • l'objet d'origine était un objet le plus dérivé (1.8) de type T et le nouvel objet est un objet le plus dérivé de type T (c'est-à-dire qu'il ne s'agit pas de sous-objets de classe de base).

Votre idée a donc été pensée par le comité des langues et spécifiquement rendue illégale, y compris la solution de contournement sournoise selon laquelle "j'ai un sous-objet de classe de base du bon type, je vais juste faire un nouvel objet à sa place", ce qui est le dernier point s'arrête sur ses traces.

Vous pouvez remplacer un objet par un objet d'un type différent comme le montre la réponse de @ RossRidge. Ou vous pouvez remplacer un objet et continuer à utiliser des pointeurs qui existaient avant le remplacement. Mais vous ne pouvez pas faire les deux ensemble.

Cependant, comme la célèbre citation: "Tout problème en informatique peut être résolu en ajoutant une couche d'indirection" et c'est vrai ici aussi.

Au lieu de votre méthode suggérée

Derived d;
Base* p = &d;
new (p) Base();  // makes p invalid!  Plus problems when d's destructor is automatically called

Tu peux faire:

unique_ptr<Base> p = make_unique<Derived>();
p.reset(make_unique<Base>());

Si vous cachez ce pointeur et cette main légère dans une autre classe, vous aurez le "modèle de conception" tel que État ou Stratégie mentionné dans d'autres réponses. Mais ils reposent tous sur un niveau d'indirection supplémentaire.

13
Ben Voigt

Vous pouvez faire ce que vous demandez littéralement avec placement new et un appel destructeur explicite. Quelque chose comme ça:

#include <iostream>
#include <stdlib.h>

class Base {
public:
    virtual void whoami() { 
        std::cout << "I am Base\n"; 
    }
};

class Derived : public Base {
public:
    void whoami() {
        std::cout << "I am Derived\n";
    }
};

union Both {
    Base base;
    Derived derived;
};

Base *object;

int
main() {
    Both *tmp = (Both *) malloc(sizeof(Both));
    object = new(&tmp->base) Base;

    object->whoami(); 

    Base baseObject;
    tmp = (Both *) object;
    tmp->base.Base::~Base();
    new(&tmp->derived) Derived; 

    object->whoami(); 

    return 0;
}

Cependant, comme l'a dit Matb, ce n'est vraiment pas un bon design. Je recommanderais de reconsidérer ce que vous essayez de faire. Certaines des autres réponses ici pourraient également résoudre votre problème, mais je pense que tout ce que l'idée de ce que vous demandez va être compliqué. Vous devez sérieusement envisager de concevoir votre application afin de pouvoir modifier le pointeur lorsque le type de l'objet change.

9
Ross Ridge

Je vous suggère d'utiliser le modèle de stratégie, par exemple.

#include <iostream>

class IAnnouncer {
public:
    virtual ~IAnnouncer() { }
    virtual void whoami() = 0;
};

class AnnouncerA : public IAnnouncer {
public:
    void whoami() override {
        std::cout << "I am A\n";
    }
};

class AnnouncerB : public IAnnouncer {
public:
    void whoami() override {
        std::cout << "I am B\n";
    }
};

class Foo
{
public:
    Foo(IAnnouncer *announcer) : announcer(announcer)
    {
    }
    void run()
    {
        // Do stuff
        if(nullptr != announcer)
        {
            announcer->whoami();
        }
        // Do other stuff
    }
    void expend(IAnnouncer* announcer)
    {
        this->announcer = announcer;
    }
private:
    IAnnouncer *announcer;
};


int main() {
    AnnouncerA a;
    Foo foo(&a);

    foo.run();

    // Ready to "expend"
    AnnouncerB b;
    foo.expend(&b);

    foo.run();

    return 0;
}

Il s'agit d'un modèle très flexible qui présente au moins quelques avantages par rapport à la résolution du problème par héritage:

  • Vous pouvez facilement changer le comportement de Foo plus tard en implémentant un nouvel annonceur
  • Vos annonceurs (et vos Foos) sont facilement testés à l'unité
  • Vous pouvez réutiliser vos Annonceurs ailleurs dans le code

Je vous suggère de jeter un œil au débat séculaire "Composition vs Héritage" (cf. https://www.thoughtworks.com/insights/blog/composition-vs-inheritance-how-choose =)

ps. Vous avez divulgué un dérivé dans votre message d'origine! Jetez un œil à std :: unique_ptr s'il est disponible.

9
D3C34C34D

Vous pouvez en introduisant une variable dans la classe de base, de sorte que l'empreinte mémoire reste la même. En définissant l'indicateur, vous forcez à appeler l'implémentation de classe dérivée ou de base.

#include <iostream>

class Base {
public:
    Base() : m_useDerived(true)
    {
    }

    void setUseDerived(bool value)
    {
        m_useDerived = value;
    }

    void whoami() {
        m_useDerived ? whoamiImpl() : Base::whoamiImpl();
    }

protected:
    virtual void whoamiImpl() { std::cout << "I am Base\n"; }

private:
    bool m_useDerived;
};

class Derived : public Base {
protected:
    void whoamiImpl() {
        std::cout << "I am Derived\n";
    }
};

Base* object;

int main() {
    object = new Derived; //assign a new Derived class instance
    object->whoami(); //this prints "I am Derived"

    object->setUseDerived(false);
    object->whoami(); //should print "I am Base"

    return 0;
}
8
Stormenet

En plus d'autres réponses, vous pouvez utiliser des pointeurs de fonction (ou n'importe quel wrapper, comme std::function) pour obtenir le comportement nécessaire:

void print_base(void) {
    cout << "This is base" << endl;
}

void print_derived(void) {
    cout << "This is derived" << endl;
}

class Base {
public:
    void (*print)(void);

    Base() {
        print = print_base;
    }
};

class Derived : public Base {
public:
    Derived() {
        print = print_derived;
    }
};

int main() {
    Base* b = new Derived();
    b->print(); // prints "This is derived"
    *b = Base();
    b->print(); // prints "This is base"
    return 0;
}

En outre, une telle approche de pointeurs de fonction vous permettrait de modifier l'une des fonctions des objets au moment de l'exécution, sans vous limiter à certains ensembles de membres déjà définis implémentés dans des classes dérivées.

7
alexeykuzmin0

J'envisagerais de régulariser votre type.

class Base {
public:
  virtual void whoami() { std::cout << "Base\n"; }
  std::unique_ptr<Base> clone() const {
    return std::make_unique<Base>(*this);
  }
  virtual ~Base() {}
};
class Derived: public Base {
  virtual void whoami() overload {
    std::cout << "Derived\n";
  };
  std::unique_ptr<Base> clone() const override {
    return std::make_unique<Derived>(*this);
  }
public:
  ~Derived() {}
};
struct Base_Value {
private:
  std::unique_ptr<Base> pImpl;
public:
  void whoami () {
    pImpl->whoami();
  }
  template<class T, class...Args>
  void emplace( Args&&...args ) {
    pImpl = std::make_unique<T>(std::forward<Args>(args)...);
  }
  Base_Value()=default;
  Base_Value(Base_Value&&)=default;
  Base_Value& operator=(Base_Value&&)=default;
  Base_Value(Base_Value const&o) {
    if (o.pImpl) pImpl = o.pImpl->clone();
  }
  Base_Value& operator=(Base_Value&& o) {
    auto tmp = std::move(o);
    swap( pImpl, tmp.pImpl );
    return *this;
  }
};

Maintenant, un Base_Value est sémantiquement un type de valeur qui se comporte de manière polymorphe.

Base_Value object;
object.emplace<Derived>();
object.whoami();

object.emplace<Base>();
object.whoami();

Vous pouvez envelopper un Base_Value instance dans un pointeur intelligent, mais cela ne me dérangerait pas.

3

Il y a une simple erreur dans votre programme. Vous affectez les objets, mais pas les pointeurs:

int main() {
    Base* object = new Derived; //assign a new Derived class instance
    object->whoami(); //this prints "I am Derived"

    Base baseObject;

Vous affectez maintenant baseObject à *object qui écrase l'objet Derived par un objet Base. Cependant, cela fonctionne bien car vous remplacez un objet de type Derived par un objet de type Base. L'opérateur d'affectation par défaut affecte simplement tous les membres, ce qui dans ce cas ne fait rien. L'objet ne peut pas changer son type et est toujours un objet Derived par la suite. En général, cela peut entraîner de graves problèmes, par exemple découpage d'objets.

    *object = baseObject; //reassign existing object to a different type
    object->whoami(); //but it *STILL* prints "I am Derived" (!)

    return 0;
}

Si vous affectez simplement le pointeur à la place, cela fonctionnera comme prévu, mais vous n'avez que deux objets, un de type Derived et un Base, mais je pense que vous voulez un comportement plus dynamique. Il semble que vous puissiez implémenter la particularité en tant que Décorateur .

Vous avez une classe de base avec une opération et plusieurs classes dérivées qui changent/modifient/étendent le comportement de la classe de base de cette opération. Puisqu'il est basé sur la composition, il peut être modifié dynamiquement. L'astuce consiste à stocker une référence de classe de base dans les instances de Decorator et à l'utiliser pour toutes les autres fonctionnalités.

class Base {
public:
    virtual void whoami() { 
        std::cout << "I am Base\n"; 
    }

    virtual void otherFunctionality() {}
};

class Derived1 : public Base {
public:
    Derived1(Base* base): m_base(base) {}

    virtual void whoami() override {
        std::cout << "I am Derived\n";

        // maybe even call the base-class implementation
        // if you just want to add something
    }

    virtual void otherFunctionality() {
        base->otherFunctionality();
    }
private:
    Base* m_base;
};

Base* object;

int main() {
    Base baseObject;
    object = new Derived(&baseObject); //assign a new Derived class instance
    object->whoami(); //this prints "I am Derived"

    // undecorate
    delete object;
    object = &baseObject; 

    object->whoami(); 

    return 0;
}

Il existe des modèles alternatifs comme Strategy qui implémentent différents cas d'utilisation resp. résoudre différents problèmes. Il serait probablement bon de lire la documentation du modèle avec un accent particulier sur les sections Intention et Motivation.

3
Jens

Je ne suis pas en désaccord avec le conseil selon lequel ce n'est pas une excellente conception, mais un autre moyen sûr de le faire est avec un syndicat qui peut contenir l'une des classes que vous souhaitez basculer entre, car la norme garantit qu'il peut contenir en toute sécurité tout d'eux. Voici une version qui résume tous les détails à l'intérieur du syndicat lui-même:

#include <cassert>
#include <cstdlib>
#include <iostream>
#include <new>
#include <typeinfo>

class Base {
public:
    virtual void whoami() { 
        std::cout << "I am Base\n"; 
    }

   virtual ~Base() {}  // Every base class with child classes that might be deleted through a pointer to the
                       // base must have a virtual destructor!
};

class Derived : public Base {
public:
    void whoami() {
        std::cout << "I am Derived\n";
    }
    // At most one member of any union may have a default member initializer in C++11, so:
    Derived(bool) : Base() {}
};

union BorD {
    Base b;
    Derived d; // Initialize one member.

    BorD(void) : b() {} // These defaults are not used here.
    BorD( const BorD& ) : b() {} // No per-instance data to worry about!
                                 // Otherwise, this could get complicated.
    BorD& operator= (const BorD& x) // Boilerplate:
    {
         if ( this != &x ) {
             this->~BorD();
             new(this) BorD(x);
         }
         return *this;
    }

    BorD( const Derived& x ) : d(x) {} // The constructor we use.
    // To destroy, be sure to call the base class’ virtual destructor,
    // which works so long as every member derives from Base.
    ~BorD(void) { dynamic_cast<Base*>(&this->b)->~Base(); }

    Base& toBase(void)
    {  // Sets the active member to b.
       Base* const p = dynamic_cast<Base*>(&b);

       assert(p); // The dynamic_cast cannot currently fail, but check anyway.
       if ( typeid(*p) != typeid(Base) ) {
           p->~Base();      // Call the virtual destructor.
           new(&b) Base;    // Call the constructor.
       }
       return b;
    }
};

int main(void)
{
    BorD u(Derived{false});

    Base& reference = u.d; // By the standard, u, u.b and u.d have the same address.

    reference.whoami(); // Should say derived.
    u.toBase();
    reference.whoami(); // Should say base.

    return EXIT_SUCCESS;
}

Un moyen plus simple d'obtenir ce que vous voulez est probablement de conserver un conteneur de Base * et remplacez les éléments individuellement selon vos besoins par new et delete. (N'oubliez pas de déclarer votre destructeur virtual! C'est important avec les classes polymorphes, donc vous appelez le bon destructeur pour cette instance, pas le destructeur de la classe de base.) Cela pourrait vous faire économiser quelques octets supplémentaires sur les instances des plus petites Des classes. Cependant, vous devrez jouer avec des pointeurs intelligents pour obtenir une suppression automatique en toute sécurité. L'un des avantages des unions par rapport aux pointeurs intelligents sur la mémoire dynamique est que vous n'avez pas à allouer ou libérer d'autres objets sur le tas, mais que vous pouvez simplement réutiliser la mémoire dont vous disposez.

1
Davislor

J'ai 2 solutions. Une plus simple qui ne conserve pas l'adresse mémoire et une qui conserve l'adresse mémoire.

Les deux nécessitent que vous fournissiez des downcasts de la base à la dérivée, ce qui n'est pas un problème dans votre cas.

struct Base {
  int a;
  Base(int a) : a{a} {};
  virtual ~Base() = default;
  virtual auto foo() -> void { cout << "Base " << a << endl; }
};
struct D1 : Base {
  using Base::Base;
  D1(Base b) : Base{b.a} {};
  auto foo() -> void override { cout << "D1 " << a << endl; }
};
struct D2 : Base {
  using Base::Base;
  D2(Base b) : Base{b.a} {};
  auto foo() -> void override { cout << "D2 " << a << endl; }
};

Pour le premier, vous pouvez créer un pointeur intelligent qui peut apparemment changer les données conservées entre les classes dérivées (et de base):

template <class B> struct Morpher {
  std::unique_ptr<B> obj;

  template <class D> auto morph() {
    obj = std::make_unique<D>(*obj);
  }

  auto operator->() -> B* { return obj.get(); }
};

int main() {
  Morpher<Base> m{std::make_unique<D1>(24)};
  m->foo();        // D1 24

  m.morph<D2>();
  m->foo();        // D2 24
}

La magie est là

m.morph<D2>();

ce qui modifie l'objet conservé en préservant les membres de données (utilise en fait le cast cast).


Si vous devez conserver l'emplacement de la mémoire, vous pouvez adapter ce qui précède pour utiliser un tampon et un nouveau placement au lieu de unique_ptr. C'est un peu plus de travail, beaucoup plus d'attention à payer, mais cela vous donne exactement ce dont vous avez besoin:

template <class B> struct Morpher {
  std::aligned_storage_t<sizeof(B)> buffer_;
  B *obj_;

  template <class D>
  Morpher(const D &new_obj)
      : obj_{new (&buffer_) D{new_obj}} {
    static_assert(std::is_base_of<B, D>::value && sizeof(D) == sizeof(B) &&
                  alignof(D) == alignof(B));
  }
  Morpher(const Morpher &) = delete;
  auto operator=(const Morpher &) = delete;
  ~Morpher() { obj_->~B(); }

  template <class D> auto morph() {
    static_assert(std::is_base_of<B, D>::value && sizeof(D) == sizeof(B) &&
                  alignof(D) == alignof(B));

    obj_->~B();
    obj_ = new (&buffer_) D{*obj_};
  }

  auto operator-> () -> B * { return obj_; }
};

int main() {
  Morpher<Base> m{D1{24}};
  m->foo(); // D1 24

  m.morph<D2>();
  m->foo(); // D2 24

  m.morph<Base>();
  m->foo(); // Base 24
}

C'est bien sûr l'os nu absolu. Vous pouvez ajouter un ctor de déplacement, un opérateur de déréférencement, etc.

0
bolov

AVERTISSEMENT: Le code ici est fourni comme un moyen de comprendre une idée, ne pas être implémenté en production.

Vous utilisez l'héritage. Il peut réaliser 3 choses:

  • Ajouter des champs
  • Ajouter des méthodes
  • remplacer les méthodes virtuelles

De toutes ces fonctionnalités, vous n'utilisez que la dernière. Cela signifie que vous n'êtes pas obligé de compter sur l'héritage. Vous pouvez obtenir les mêmes résultats par de nombreux autres moyens. Le plus simple est de garder un œil sur le "type" par vous-même - cela vous permettra de le changer à la volée:

#include <stdexcept>

enum MyType { BASE, DERIVED };

class Any {
private:
    enum MyType type;
public:
    void whoami() { 
        switch(type){
            case BASE:
                std::cout << "I am Base\n"; 
                return;
            case DERIVED:
                std::cout << "I am Derived\n"; 
                return;
        }
        throw std::runtime_error( "undefined type" );
    }
    void changeType(MyType newType){
        //insert some checks if that kind of transition is legal
        type = newType;
    }
    Any(MyType initialType){
        type = initialType;
    }

};

Sans héritage, le "type" est à vous de faire ce que vous voulez. Vous pouvez changeType à tout moment qui vous convient. Avec cette puissance vient aussi la responsabilité: le compilateur ne s'assurera plus que le type est correct ou même défini du tout. Vous devez vous en assurer ou vous aurez du mal à déboguer les erreurs d'exécution.

Vous pouvez également l'envelopper dans l'héritage, par exemple. pour obtenir un remplacement direct pour le code existant:

class Base : Any {
public:
    Base() : Any(BASE) {}
};

class Derived : public Any {
public:
    Derived() : Any(DERIVED) {}
};

OU (légèrement plus laid):

class Derived : public Base {
public:
    Derived : Base() {
        changeType(DERIVED)
    }
};

Cette solution est facile à mettre en œuvre et à comprendre. Mais avec plus d'options dans le commutateur et plus de code dans chaque chemin, cela devient très compliqué. Ainsi, la toute première étape consiste à refactoriser le code réel hors du commutateur et à intégrer des fonctions autonomes. Quel meilleur endroit à conserver que la classe Derivied?

class Base  {
public:
    static whoami(Any* This){
        std::cout << "I am Base\n"; 
    }
};

class Derived  {
public:
    static whoami(Any* This){
        std::cout << "I am Derived\n"; 
    }
};

/*you know where it goes*/
    switch(type){
        case BASE:
            Base:whoami(this);
            return;
        case DERIVED:
            Derived:whoami(this);
            return;
    }

Ensuite, vous pouvez remplacer le commutateur par une classe externe qui l'implémente via l'héritage virtuel et TADA! Nous avons réinventé le modèle de stratégie, comme d'autres l'ont dit en premier lieu:)

L'essentiel est: quoi que vous fassiez, vous n'héritez pas de la classe principale.

0
Agent_L

vous ne pouvez pas changer le type d'un objet après l'instanciation, comme vous pouvez le voir dans votre exemple, vous avez un pointeur vers une classe de base (de type classe de base) donc ce type y est collé jusqu'à la fin.

  • le pointeur de base peut pointer vers un objet supérieur ou inférieur ne signifie pas que son type a changé:

    Base* ptrBase; // pointer to base class (type)
    ptrBase = new Derived; // pointer of type base class `points to an object of derived class`
    
    Base theBase;
    ptrBase = &theBase; // not *ptrBase = theDerived: Base of type Base class points to base Object.
    
  • les pointeurs sont beaucoup plus forts, flexibles, puissants autant dangereux que vous devez donc les manipuler avec prudence.

dans votre exemple je peux écrire:

Base* object; // pointer to base class just declared to point to garbage
Base bObject; // object of class Base
*object = bObject; // as you did in your code

au-dessus, c'est un désastre qui affecte la valeur au pointeur non alloué. le programme va planter.

dans votre exemple, vous avez échappé au crash grâce à la mémoire qui était allouée au début:

object = new Derived;

ce n'est jamais une bonne idée d'attribuer un value and not address d'un objet de sous-classe à la classe de base. cependant dans intégré vous pouvez mais considérez cet exemple:

int* pInt = NULL;

int* ptrC = new int[1];
ptrC[0] = 1;

pInt = ptrC;

for(int i = 0; i < 1; i++)
    cout << pInt[i] << ", ";
cout << endl;

int* ptrD = new int[3];
ptrD[0] = 5;
ptrD[1] = 7;
ptrD[2] = 77;

*pInt = *ptrD; // copying values of ptrD to a pointer which point to an array of only one element!
// the correct way:
// pInt = ptrD;

for(int i = 0; i < 3; i++)
    cout << pInt[i] << ", ";
cout << endl;

donc le résultat n'est pas comme vous le pensez.

0
Raindrop7