指针工作,然后似乎无缘无故地中断

Pointer Works And Then Breaks Seemingly Without Reason

本文关键字:无缘无故 中断 工作 然后 指针      更新时间:2023-10-16

为什么我可以在分配函数的作用域内时访问分配指针后访问对象中保存的数据,但一旦我尝试通过同一指针访问相同的值,但在该函数之外就不能了?

指针是一个成员函数,在函数内分配。在函数中,工作正常。但是,当从该函数中调用另一个使用类作用域的函数时,指针行为异常,呈现垃圾数据。为什么?

法典:

#include <iostream>
class Object{
public:
    Object(  ) { std::cout << "Object constructor called." << std::endl; }
    Object( const Object &in ) { i = in.i; std::cout << "Object copy constructor called." << std::endl; }
    ~Object(  ) { std::cout << "Object destructor called." << std::endl; }
    int i;
};
class ObjectManager{
public:
    Object * retObject(){
        Object *myObject = new Object;
        myObject->i=55;
        return myObject;
    }
};
class LogicManager{
public:
    LogicManager(){
        myObjectManager = new ObjectManager;
    }
    ~LogicManager(){
        delete myObjectManager;
    }
    Object * retObject(){
        return myObjectManager->retObject();
    }
private:
    ObjectManager *myObjectManager;
};
class Viewer{
public:
    ~Viewer(  ) { if( myObject ) { delete myObject; } }
    void ptrinObject( LogicManager * inLogic ){
        myObject = inLogic->retObject();
        std::cout << "Got path size of " << myObject->i << std::endl;       //correct
        std::cout << "Got path size of " << retObjectVal(  ) << std::endl;  //0?!?!?!?
    }
    int retObjectVal(  ) { myObject->i; }
private:
    Object *myObject;
};

int main(){
    LogicManager myManager;
    Viewer myViewer;
    //myViewer.cpyinObject( &myManager );
    myViewer.ptrinObject( &myManager );
    return 0;
}

输出:

Object constructor called.
Got path size of 55
Got path size of 861280848
Object destructor called.

问题是:

int retObjectVal(  ) { myObject->i; }

该函数中没有return语句。你只有一个没有副作用的表情。结果,我们遇到了 §6.6.3/2:

从函数末尾流出等效于没有值的返回;这导致未定义 值返回函数中的行为。

返回的内容是未定义的行为,因此它最终返回了一些垃圾。只需制作:

int retObjectVal() { return myObject->i; }

这是一个容易犯的错误,这就是为什么您应该始终尝试使用尽可能高的警告设置进行编译的原因。例如,在没有标志的 gcc 上,我没有收到任何警告。但是有了-Wall,我得到:

警告:语句无效 [-Wunused-value]

警告:函数中没有返回语句返回非 void [-Wreturn 类型]

首先,你没有初始化Viewer::myObject所以如果你只是这样做

void something() {
    Viewer v;
}

您最终可能会尝试删除无效指针。

myViewer.cpyinObject( &myManager );
myViewer.ptrinObject( &myManager );

这两个函数都会创建一个新Object但都不会检查是否已分配一个(内存泄漏)。然后,他们立即忽略创建的新对象,而是分配另一个由ObjectManager分配的新Object(更多内存泄漏)。

最后,Viewer::retObjectVal实际上并没有专门return一个值,因此您会收到"函数末尾的垃圾"。

我建议你看看你的编译器警告,因为任何明智的编译器都会警告你我提到的第一个和第三个问题。

Viewer类中,您缺少一个return语句:

int retObjectVal( ) { return myObject->i; }

应该工作。

您拥有的是一种方法,其中某些分支在不返回值的情况下脱落。这会导致未定义的行为。你只是在评估"myObject->i;"作为一个语句。


您可能应该解决的另一个问题是,您的 Object 指针不属于任何人,也不会在任何地方删除。也就是说,您正在删除ObjectManager但它没有任何地方delete基础对象本身。您可能应该在那里找出一个所有权模型,让某人跟踪这些指针,并在适当的时候delete它们。