Почему этот код печатает только 42? - PullRequest
6 голосов
/ 18 июля 2009

Может кто-нибудь объяснить мне, почему этот код печатает только "42" вместо "созданный \ n42"?

#include <iostream>
#include <string>
#include <memory>

using namespace std;

class MyClass
{
public:
    MyClass() {cout<<"created"<<endl;};
    int solution() {return 42;}
    virtual ~MyClass() {};
};

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr;
    cout<<ptr->solution()<<endl;
    return 0;
}

Кстати, я пробовал этот код с разными значениями в решении, и я всегда получаю «правильное» значение, так что это не случайное счастливое значение.

Ответы [ 7 ]

27 голосов
/ 18 июля 2009

Поскольку он демонстрирует неопределенное поведение - вы разыменовываете нулевой указатель.

Когда вы говорите:

 auto_ptr<MyClass> ptr;

вы создаете автоматический указатель, который ни на что не указывает. Это равносильно высказыванию:

MyClass * ptr = NULL;

Тогда, когда вы говорите:

cout<<ptr->solution()<<endl;

вы разыменовываете этот нулевой указатель. Это не определено в C ++ - для вашей реализации это работает.

21 голосов
/ 18 июля 2009

std :: auto_ptr не будет автоматически создавать объект для вас. То есть ptr в основном в том виде, в каком оно есть, инициализируется нулем. Разыменование это неопределенное поведение, и вам просто повезло, и в результате вы получили 42.

Если вы действительно создаете объект:

int main(int argc, char *argv[])
{
    auto_ptr<MyClass> ptr(new MyClass);

    cout << ptr->solution() << endl;

    return 0;
}

Вы получите ожидаемый результат.

3 голосов
/ 19 июля 2009

Во-первых, имейте в виду, что оператор -> для auto_ptr по существу перенаправляется на содержащийся указатель. Итак, для этого обсуждения ваш код в main становится эквивалентным:

MyClass* ptr = NULL;
cout << ptr->solution() << endl;

Затем обратите внимание, что компиляторы имеют тенденцию реализовывать функции-члены способами, которые действуют очень похоже на то, как если бы они были функциями, не являющимися членами, с указателем this, переданным в качестве другого аргумента функции. Таким образом, с точки зрения вашего текущего компилятора, ваш код в main действует так, как если бы он был:

MyClass* ptr = NULL;
cout << solution(ptr) << endl;

с решением, записанным как:

int solution(MyClass* this) { return 42; }

В этом случае становится очевидным, почему не произошло крушения.


Однако, как уже упоминали другие, это внутренние детали того, как компиляторы реализуют C ++, которые не определены стандартом языка. Таким образом, теоретически этот код может работать, как описано здесь, на одном компиляторе, но аварийно завершать работу или делать что-то другое полностью на другом компиляторе.

Но на практике, даже если стандарт не гарантирует такое поведение, любой конкретный компилятор может гарантировать его, если захочет. Например: поскольку MFC использует это поведение, маловероятно, что Visual Studio когда-либо перестанет его поддерживать. Конечно, вам придется исследовать каждый конкретный компилятор, где может использоваться ваш код, чтобы убедиться, что он действительно гарантирует такое поведение.

2 голосов
/ 18 июля 2009

Вы не создаете экземпляр объекта.
Вы только создаете умный указатель.

Когда вы вызываете метод, вы отменяете ссылку на указатель NULL, так как, как упоминал Нил, вы находитесь в неопределенном поведении. Но поскольку ваш код не пытается получить доступ к каким-либо переменным-членам, он, к счастью, не падает.

Попробуйте это:

auto_ptr<MyClass> ptr(new MyClass);
2 голосов
/ 18 июля 2009

Потому что вы не знаете вопрос к ответу xD

Кажется, вы не вызываете конструктор, верно?

1 голос
/ 18 июля 2009

Вы не получите сбой, потому что метод «решения» не должен фактически использовать членов класса. Если бы вы возвращали участника или что-то еще, вы, вероятно, получили бы сбой.

1 голос
/ 18 июля 2009

Потому что ptr неинициализирован, и вам повезло. Вы должны сначала позвонить new для этого:

auto_ptr<MyClass> ptr( new MyClass );
...