2012-05-22 3 views
2

Les résultats suivants sont très intéressants et j'ai du mal à les comprendre. Fondamentalement, j'ai une classe qui a un int:Comportement étrange des pointeurs en C++

class TestClass{ 
public: 
    int test; 
    TestClass() { test = 0; }; 
    TestClass(int _test) { test = _test; }; 
    ~TestClass() { /*do nothing*/ }; 
}; 

Une fonction de test qui accepte un pointeur de TestClass

void testFunction1(TestClass *ref){ 
    delete ref; 
    TestClass *locTest = new TestClass(); 
    ref = locTest; 
    ref->test = 2; 
    cout << "test in testFunction1: " << ref->test << endl; 
} 

C'est ce que je fais dans le principal:

int main(int argc, _TCHAR* argv[]) 
{ 
    TestClass *testObj = new TestClass(1); 
    cout << "test before: " << testObj->test << endl; 
    testFunction1(testObj); 
    cout << "test after: " << testObj->test << endl; 
    return 0; 
} 

I s'attendait à ce que la production soit:

test before: 1 
test in testFunction1: 2 
test after: 1 

Mais je reçois la sortie suivante:

test before: 1 
test in testFunction1: 2 
test after: 2 

Quelqu'un peut-il expliquer cela. Chose intéressante est que le changement testFunction1 à:

void testFunction1(TestClass *ref){ 
    //delete ref; 
    TestClass *locTest = new TestClass(); 
    ref = locTest; 
    ref->test = 2; 
    cout << "test in testFunction1: " << ref->test << endl; 
} 

-à-dire que je ne supprimer ref avant pointant vers un nouvel emplacement, je reçois la sortie suivante:

test before: 1 
test in testFunction1: 2 
test after: 1 

J'apprécierais vraiment si quelqu'un peut me expliquer ce comportement étrange. Merci.

+0

aucun changement ne fait dans le principal – umair

Répondre

4

Lorsque vous accédez à l'objet après l'avoir supprimé, le comportement n'est pas défini.

Le comportement que vous voyez résulte de l'algorithme d'allocation de mémoire de votre système.

Après la suppression de votre premier objet testclass, vous allouez de la mémoire à un nouvel objet. L'exécution réutilise simplement la mémoire.

Pour vérifier ce qui se passe, imprimer les valeurs des pointeurs:

void testFunction1(TestClass *ref){ 
    cout << ref << endl; 
    delete ref; 
    TestClass *locTest = new TestClass(); 
    cout << locTest << endl; 
    ref = locTest; 
    ref->test = 2; 
    cout << "test in testFunction1: " << ref->test << endl; 
} 
4

Vous obtenez une copie du pointeur sur l'objet dans testFunction1(), donc quand vous lui attribuez, la valeur initiale du pointeur dans main() ne change pas

, vous aussi supprimez l'objet (en appelant delete dans testFunction1()) que le pointeur d'origine (main()) pointe, mais comme la valeur dans main() n'est pas mise à jour, vous accédez à un objet non valide - le fait que vous pouvez lire la valeur que vous définissez dans testFunction1(), est une coïncidence et ne peut pas être invoqué

Le fait que vous avez lu correctement l'original val ue dans le second cas (lorsque vous n'appelez pas delete) est parce que l'objet original n'a pas été changé (vous en changez un nouveau en testFinction1 et le pointeur en main est le même (comme expliqué ci-dessus) et l'objet est toujours vivant

+0

Exactement, et la fuite de mémoire est dans le testFunction1() depuis vous ne jamais obtenir l'adresse de la mémoire nouvellement créée en dehors de la fonction. – Brady

+0

mais pourquoi la valeur de pointeur dans main est mise à jour de 1 à 2 si nous passons une copie à testFunction1() – umair

+3

@umair: C'est un comportement indéfini. Vous n'avez aucun droit à toute attente. –

0

Les chances sont le nouvel objet (avec un 2) est en cours de création où l'ancien objet supprimé (avec un 1) utilisé pour être. Votre pointeur d'origine pointe toujours vers cet emplacement. Lorsque vous y accédez, vous accédez accidentellement au nouvel objet (avec un 2).

1

Après cette instruction:

TestClass *testObj = new TestClass(1); 

que vous avez alloué une nouvelle zone de mémoire contenant un objet TestClass, dont l'adresse (appelons-le maddr) est stocké dans testObj.

Maintenant, cette instruction:

cout << "test before: " << testObj->test << endl; 

sorties 1, comme prévu.

À l'intérieur testFunction1() vous avez une variable locale appelée ref, qui est un pointeur contenant la valeur maddr. Lorsque vous delete ref, vous libérez la zone de mémoire contenant un objet TestClass, dont l'adresse est maddr.

Ensuite, vous allouent une nouvelle zone de mémoire:

TestClass *locTest = new TestClass(); 

et locTest contient son adresse, appelons-le m1addr.

Ensuite, vous utilisez ref pour accéder à la zone de mémoire à m1addr et modifiez la valeur int test à 2.

Cette instruction:

cout << "test in testFunction1: " << ref->test << endl; 

sorties 2 comme prévu.

Maintenant, retour à main, vous avez perdu tout gestionnaire à la zone contenant un objet TestClass dont l'adresse est m1addr (qui est, vous êtes une fuite de mémoire) et la zone pointée par testObj n'est pas alloué plus. Lorsque vous utilisez à nouveau testObj, vous accédez à une zone de mémoire commençant à maddr, qui a été nettoyée.

L'effet d'accéder à testObj->test est un comportement indéfini. Ce que vous ressentez peut être lié au fait que lorsque vous exécutez votre code maddr == m1addr, mais cela ne peut arriver que par hasard, vous ne pouvez pas compter sur cela.

0

TestClass * ref dans le paramètre de testFunction1 et TestClass * testObj dans main sont 2 pointeurs différents à la même chose, ils ne sont pas le même pointeur cependant. Si vous souhaitez supprimer et réallouer un objet dans une fonction/méthode, vous pouvez utiliser un pointeur sur un pointeur comme paramètre.

Comme d'autres l'ont mentionné, après testfunction1 vous accédez à un objet qui a été supprimé dans testfunction1 qui, comme mentionné également, est un comportement indéfini. La mémoire pointée par le pointeur dangling a été libérée pendant la suppression mais le contenu est susceptible d'être encore là jusqu'à ce que l'emplacement de mémoire soit réaffecté pendant un autre appel à nouveau. Donc, vous utilisez un espace non alloué qui pourrait être écrasé très facilement à tout moment.

Hope this helps

2

dans ce cas, vous venez de recevoir le nouvel objet à la même adresse avec l'ancien vous supprimez.

En fait, testObj est devenu un pointeur suspendu après l'appel de testFunction1.

void testFunction1(TestClass *ref){ 
    delete ref; 
    TestClass *locTest = new TestClass(); 
    cout << "locTest = " << locTest << endl; 
    ref = locTest; 
    ref->test = 2; 
    cout << "test in testFunction1: " << ref->test << endl; 
} 

int main(int argc, char * argv[]) 
{ 
    TestClass *testObj = new TestClass(1); 
    cout << "test before: " << testObj->test << endl; 
    cout << "testObg = " << testObj << endl; 
    testFunction1(testObj); 
    cout << "test after: " << testObj->test << endl; 
    cout << "testObg = " << testObj << endl; 
    return 0; 
} 

sortie est:

test before: 1 
testObg = 0x511818 
locTest = 0x511818 
test in testFunction1: 2 
test after: 2 
testObg = 0x511818