Вопрос по c++ – Странное поведение указателей в с ++

2

Следующие результаты очень интересны, и мне трудно их понять. В основном у меня есть класс, который имеет int:

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

Тестовая функция, которая принимает указатель TestClass

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

Это то, что я делаю в основном:

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;
}

Я ожидал, что результат будет:

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

Но я получаю следующий вывод:

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

Может кто-нибудь объяснить это. Интересно, что изменение testFunction1 на:

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

То есть. Я не удаляю ref, прежде чем указывать его на новое место, я получаю следующий вывод:

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

Я был бы очень признателен, если бы кто-нибудь объяснил мне это странное поведение. Благодарность

не внесены изменения в основной umair

Ваш Ответ

6   ответов
4

Поведение, которое вы видите, следует из алгоритма выделения памяти в вашей системе.

То есть после удаления вашего первого объекта testclass вы выделяете память для нового объекта. Среда выполнения просто повторно использует память.

Чтобы проверить, что происходит, выведите значения указателей:

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

testFunction1(), поэтому при присвоении ему исходное значение указателя вmain() не меняетс

Также вы удаляете объект (вызываяdelete вtestFunction1()) на который указывает исходный указатель (вmain()) указывает, но как значение вmain() не обновляется, вы обращаетесь к недопустимому объекту - тот факт, что вы можете прочитать значение, которое вы установили вtestFunction1(), это совпадение и на него нельзя положить

Тот факт, что вы правильно прочитали исходное значение во втором случае (когда вы не звонитеdelete) потому что исходный объект не был изменен (вы изменили новый вtestFinction1 и указатель на него вmain - то же самое (как объяснено выше), и объект все еще жив

Точно, и утечка памяти находится в testFunction1 (), так как вы никогда не получите адрес вновь созданной памяти вне функции. Brady
Но почему значение указателя в main обновляется с 1 до 2, если мы передаем копию в testFunction1 () umair
@ umair: это неопределенное поведение. Вы не имеете права на какие-либо ожидания. Benjamin Lindley
@ umair Значение указателя не меняется. Вы думаете о указателе, как будто это был объект. Представьте, что у вас было 3 ведра с водой. Ваш указатель указывает на ведро 2. Теперь вы опорожняете и заправляете ведро 2 маслом (testFunction1). Теперь, когда указатель указывает на ведро, вы получаете масло, а не воду. sji
@ sji - кроме как вtestFunction1 ОП создает новое «ведро», поэтому вы по-прежнему получаете «воду», а не «масло» (то есть, если исходное «Ведро» не уничтожено Attila
2

что и старый, который вы удалили.

Фактически testObj стал висящим указателем после вызова 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;
}

Выход:

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

После этой инструкции:

TestClass *testObj = new TestClass(1);

Вы выделили новую область памяти, содержащуюTestClass объект, чей адрес (назовем егоmaddr) хранится вtestObj.

Теперь эта инструкция:

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

outputs1, как и ожидалось

ВнутриtestFunction1() у вас есть локальная переменная с именемref, который является указателем, содержащим значениеmaddr.

Когда тыdelete ref, вы освобождаете область памяти, содержащуюTestClass объект, адрес которогоmaddr.

Затем вы выделяете новую область памяти:

TestClass *locTest = new TestClass();

а такжеlocTest содержит адрес, назовем егоm1addr.

Тогда ты используешьref для доступа к области памяти наm1addr и поменяйint test значение2.

Эта инструкция:

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

outputs2 как и ожидалось

Теперь вернемся кmain, вы потеряли любой обработчик для области, содержащейTestClass объект с адресомm1addr (то есть у вас течет память) и область, на которую указываетtestObj больше не выделяется.

Когда ты используешьtestObj снова вы получаете доступ к области памяти, начинающейся сmaddr, который был очищен. Эффект доступаtestObj->test - неопределенное поведение.

То, что вы испытываете, может быть связано с тем, что когда вы запускаете свой кодmaddr == m1addr, но это может произойти только случайно, на это нельзя полагаться.

0

где раньше был старый удаленный объект (с 1). Ваш исходный указатель по-прежнему указывает на это местоположение, поэтому при доступе к нему вы случайно получаете доступ к новому объекту (с 2).

0

- это два разных указателя на одну и ту же вещь, хотя они и не совпадают. Если вы хотите удалить и перераспределить объект внутри функции / метода, вы можете использовать указатель на указатель в качестве параметра.

Как уже упоминали другие, после testfunction1 вы получаете доступ к объекту, который был удален внутри testfunction1, что, как уже упоминалось, является неопределенным поведением. Память, на которую указывает свисающий указатель, была освобождена во время удаления, но содержимое, вероятно, все еще будет там, пока место памяти не будет перераспределено во время другого вызова new. Таким образом, вы используете нераспределенное пространство, которое может быть легко перезаписано в любое время.

Надеюсь это поможе

Похожие вопросы