Si je crée une classe MyClass et qu'un membre privé dit MyOtherClass, est-il préférable de faire de MyOtherClass un pointeur ou non? Qu'est-ce que cela signifie aussi de l'avoir comme pointeur non plus en ce qui concerne l'endroit où il est stocké en mémoire? L'objet sera-t-il créé lors de la création de la classe?
J'ai remarqué que les exemples de QT déclarent généralement les membres de la classe comme des pointeurs lorsqu'ils sont des classes.
Cordialement
Marque
Si je crée une classe MyClass et qu'un membre privé dit MyOtherClass, est-il préférable de faire de MyOtherClass un pointeur ou non?
vous devriez généralement le déclarer comme une valeur dans votre classe. ce sera local, il y aura moins de risque d'erreurs, moins d'allocations - en fin de compte, moins de choses qui pourraient mal tourner, et le compilateur peut toujours savoir qu'il est là à un décalage spécifié donc ... cela aide l'optimisation et la réduction binaire à un quelques niveaux. il y aura quelques cas où vous saurez que vous devrez gérer un pointeur (c'est-à-dire polymorphe, partagé, nécessite une réaffectation), il est généralement préférable d'utiliser un pointeur uniquement lorsque cela est nécessaire - en particulier lorsqu'il est privé/encapsulé.
Qu'est-ce que cela signifie aussi de l'avoir comme pointeur non plus en ce qui concerne l'endroit où il est stocké en mémoire?
son adresse sera proche de (ou égale à) this
- gcc (par exemple) a quelques options avancées pour vider les données de classe (tailles, vtables, décalages)
L'objet sera-t-il créé lors de la création de la classe?
oui - la taille de MyClass augmentera en fonction de sizeof (MyOtherClass), ou davantage si le compilateur le réaligne (par exemple, sur son alignement naturel)
Jetez un oeil à cet exemple:
struct Foo { int m; };
struct A {
Foo foo;
};
struct B {
Foo *foo;
B() : foo(new Foo()) { } // ctor: allocate Foo on heap
~B() { delete foo; } // dtor: Don't forget this!
};
void bar() {
A a_stack; // a_stack is on stack
// a_stack.foo is on stack too
A* a_heap = new A(); // a_heap is on stack (it's a pointer)
// *a_heap (the pointee) is on heap
// a_heap->foo is on heap
B b_stack; // b_stack is on stack
// b_stack.foo is on stack
// *b_stack.foo is on heap
B* b_heap = new B(); // b_heap is on stack
// *b_heap is on heap
// b_heap->foo is on heap
// *(b_heap->foo is on heap
delete a_heap;
delete b_heap;
// B::~B() will delete b_heap->foo!
}
Nous définissons deux classes A
et B
. A
stocke un membre public foo
de type Foo
. B
a un membre foo
de type pointer to Foo
.
Quelle est la situation pour A
:
a_stack
de type A
sur stack, l'objet (évidemment) et ses membres se trouvent également sur stack.A
comme a_heap
dans l'exemple ci-dessus, seule la variable de pointeur se trouve sur le stack; tout le reste (l'objet et ses membres) est sur le heap.À quoi ressemble la situation dans le cas de B
:
B
sur stack: alors l'objet et son membre foo
se trouvent sur stack, mais l'objet sur lequel foo
pointe (la pointee) se trouve sur heap. En bref: b_stack.foo
(le pointeur) est sur la pile, mais *b_stack.foo
le (pointee) est sur le tas.B
nommé b_heap
: b_heap
(le pointeur) est sur la pile, *b_heap
(la pointee) est sur le heap, ainsi que le membre b_heap->foo
et *b_heap->foo
.foo
sera automatiquement créé en appelant le constructeur implicite par défaut de Foo
. Cela créera une integer
mais pas l'initialisera (il aura un nombre aléatoire)!foo
(le pointeur) sera également créé et initialisé avec un nombre aléatoire, ce qui signifie qu'il pointe vers un emplacement random sur le tas. Mais notez que le pointeur existe! Notez également que le constructeur implicite par défaut n'allouera rien pour foo
pour vous, vous devez le faire explicitement. C'est pourquoi vous avez généralement besoin d'un constructeur explicit et d'un destructor associé pour allouer et supprimer la pointe de votre pointeur de membre. N'oubliez pas la sémantique copy: qu'advient-il de la pointee si vous copiez l'objet (via la construction de copie ou l'affectation)?Il existe plusieurs cas d'utilisation d'un pointeur sur un membre:
Faites très attention si vos membres sont des pointeurs et que vous les possédez. Vous devez écrire les constructeurs et les destructeurs appropriés et penser aux constructeurs de copie et aux opérateurs d'affectation. Qu'advient-il de la pointee si vous copiez l'objet? Habituellement, vous devrez également construire la pointee!
En C++, les pointeurs sont des objets à part entière. Ils ne sont pas vraiment liés à ce qu'ils désignent, et il n'y a pas d'interaction particulière entre un pointeur et sa pointee (est-ce un mot?)
Si vous créez un pointeur, vous créez un pointeur et rien d’autre . Vous ne créez pas l'objet sur lequel il peut ou non pointer. Et lorsqu'un pointeur sort du cadre, l'objet pointé n'est pas affecté. Un pointeur n'affecte en aucun cas la durée de vie de tout ce qu'il pointe.
Donc, en général, vous devriez pas utiliser les pointeurs par défaut. Si votre classe contient un autre objet, cet autre objet ne doit pas être un pointeur.
Cependant, si votre classe est au courant de d'un autre objet, un pointeur peut être un bon moyen de le représenter (car plusieurs instances de votre classe peuvent alors pointer sur la même instance, sans en prendre possession, et sans contrôler sa durée de vie)
La sagesse courante en C++ consiste à éviter autant que possible d'utiliser des pointeurs (nus). Des pointeurs particulièrement simples pointant vers la mémoire allouée dynamiquement.
La raison en est que les pointeurs rendent plus difficile l’écriture de classes robustes, en particulier lorsque vous devez également envisager la possibilité de lever des exceptions.
Quelques avantages du membre de pointeur:
Avantages d'avoir le membre comme objet:
Cette question pourrait être délibérée à l'infini, mais l'essentiel est:
Si MyOtherClass n'est pas un pointeur:
Si MyOtherClass est un pointeur:
NULL
, ce qui pourrait avoir une signification dans votre contexte et économiser de la mémoire.Je suis la règle suivante: si l’objet membre vit et meurt avec l’objet encapsulant, n’utilisez pas de pointeur. Vous aurez besoin d'un pointeur si l'objet membre doit survivre à l'objet encapsulant pour une raison quelconque. Cela dépend de la tâche à accomplir.
Habituellement, vous utilisez un pointeur si l'objet membre vous est donné et n'est pas créé par vous. Ensuite, vous n'avez généralement pas à le détruire non plus.
Si vous créez l'objet MyOtherClass en tant que membre de votre MyClass:
size of MyClass = size of MyClass + size of MyOtherClass
Si vous créez l'objet MyOtherClass en tant que membre du pointeur de votre MyClass:
size of MyClass = size of MyClass + size of any pointer on your system
Vous voudrez peut-être conserver MyOtherClass en tant que membre de pointeur car cela vous donne la possibilité de le diriger vers toute autre classe dérivée de celle-ci. Fondamentalement, vous aide à implémenter le polymorphisme dynamice.
Ça dépend... :-)
Si vous utilisez des pointeurs pour indiquer un class A
, vous devez créer un objet de type A, par exemple. dans le constructeur de votre classe
m_pA = new A();
De plus, n'oubliez pas de détruire l'objet dans le destructeur ou vous avez une fuite de mémoire:
delete m_pA;
m_pA = NULL;
Au lieu de cela, avoir un objet de type A agrégé dans votre classe est plus facile, vous ne pouvez pas oublier de le détruire, car cela se fait automatiquement à la fin de la vie de votre objet.
D'autre part, avoir un pointeur présente les avantages suivants:
Si votre objet est alloué sur la pile Et que le type A utilise beaucoup de mémoire , Il ne sera pas alloué à partir de la pile Mais à partir du tas.
Vous pouvez construire votre objet A plus tard (par exemple, dans une méthode Create
) ou le détruire plus tôt (dans la méthode Close
)
Un avantage du fait que la classe parent conserve la relation avec un objet membre en tant que pointeur (std :: auto_ptr) sur l'objet membre est que vous pouvez transférer la déclaration de l'objet plutôt que de devoir inclure le fichier d'en-tête de l'objet.
Cela permet de découpler les classes au moment de la construction, ce qui permet de modifier la classe d'en-tête de l'objet membre sans provoquer la recompilation de tous les clients de votre classe parent, même s'ils n'ont probablement pas accès aux fonctions de l'objet membre.
Lorsque vous utilisez un auto_ptr, vous devez uniquement vous occuper de la construction, ce que vous pouvez généralement faire dans la liste des initialiseurs. La destruction avec l'objet parent est garantie par auto_ptr.
La chose simple à faire est de déclarer vos membres en tant qu'objets. De cette façon, vous n'avez pas à vous soucier de la construction, de la destruction et de l'affectation des copies. Tout cela est pris en charge automatiquement.
Cependant, il y a encore des cas où vous voulez des pointeurs. Après tout, les langages gérés (tels que C # ou Java) contiennent en réalité des objets membres par des pointeurs.
Le cas le plus évident est celui où l'objet à conserver est polymorphe. Comme vous l'avez fait remarquer, dans Qt, la plupart des objets appartiennent à une énorme hiérarchie de classes polymorphes et il est obligatoire de les tenir par des pointeurs, car vous ne savez pas à l'avance quelle taille aura l'objet membre.
Méfiez-vous des pièges courants dans ce cas, en particulier lorsque vous utilisez des classes génériques. La sécurité des exceptions est une préoccupation majeure:
struct Foo
{
Foo()
{
bar_ = new Bar();
baz_ = new Baz(); // If this line throw, bar_ is never reclaimed
// See copy constructor for a workaround
}
Foo(Foo const& x)
{
bar_ = x.bar_.clone();
try { baz_ = x.baz_.clone(); }
catch (...) { delete bar_; throw; }
}
// Copy and swap idiom is perfect for this.
// It yields exception safe operator= if the copy constructor
// is exception safe.
void swap(Foo& x) throw()
{ std::swap(bar_, x.bar_); std::swap(baz_, x.baz_); }
Foo& operator=(Foo x) { x.swap(*this); return *this; }
private:
Bar* bar_;
Baz* baz_;
};
Comme vous le voyez, il est assez fastidieux d’avoir des constructeurs protégés contre les exceptions en présence de pointeurs. Vous devriez regarder RAII et les pointeurs intelligents (il existe de nombreuses ressources ici et ailleurs sur le Web).