(C++)返回函数内部声明的对象的地址
(C++) Returning addresses of objects declared inside functions
下面是我读过并知道的一些假设事实。
1) 在函数内创建的对象(忽略新的/动态内存)不能返回(函数完成后它们消失)
示例:
std::string* getTestString()
{
std::string s = "STRING MADE IN FUNCTION";
std::cout << "(INSIDE FUNCTION) DEREFERENCE (ADDRESS OF STRING): " << *(&s) << std::endl;
std::cout << "(INSIDE FUNCTION) RETURN (ADDRESS OF STRING): " << &s << std::endl;
return &s;
}
//main function
{
std::string* s = getTestString();
std::cout << "(IN MAIN) STRING POINTER: " << s << std::endl;
std::cout << "(IN MAIN) ADDRESS OF (STRING POINTER): " << &s << std::endl;
std::cout << "(IN MAIN) DEREFERENCE OF (STRING POINTER): " << *s << std::endl;
}
控制台的结果:
(INSIDE FUNCTION) DEREFERENCE (ADDRESS OF STRING): STRING MADE IN FUNCTION
(INSIDE FUNCTION) RETURN (ADDRESS OF STRING): 0x7fff85791040
(IN MAIN) STRING POINTER: 0x7fff85791040
(IN MAIN) ADDRESS OF (STRING POINTER): 0x7fff85791088
The program has unexpectedly finished.
总结:
1) 字符串s在函数中生成
2) 字符串返回的ADDRESS
3) 由于函数已完成,字符串s和内容已完成
4) 通常,返回字符串s的ADDRESS,但它所指向的是GONE
5) 你试图取消对该地址的引用,由于这些东西已经消失,它会导致崩溃
这是有道理的。
2) 这个例子:
class TestObject
{
public:
TestObject();
~TestObject();
int getTestVariable();
private:
int testVariable = 9999;
};
//在MAIN.CPP 中
TestObject testTestObject1()
{
TestObject testObject;
std::cout<<"(INSIDE FUNCTION) ADDRESS OF TESTOBJECT: "<<&testObject<<std::endl;
std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
return testObject;
}
TestObject* testTestObject2()
{
TestObject testObject;
std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): "<<&testObject<<std::endl;
std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
return &testObject;
}
{
TestObject tObject1 = testTestObject1();
std::cout << tObject1.getTestVariable() << std::endl;
std::cout << "(IN MAIN) ADDRESS OF (TESTOBJECT): " << &tObject1 << std::endl;
TestObject* tObject2 = testTestObject2();
std::cout << tObject2->getTestVariable() << std::endl;
std::cout << "(IN MAIN) TESTOBJECT POINTER: " << tObject2 << std::endl;
}
控制台中的结果:
(INSIDE FUNCTION) ADDRESS OF TESTOBJECT: 0x7fff1aa91520
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999
(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7fff1aa91520
(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x7fff1aa914e0
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999
(IN MAIN) TESTOBJECT POINTER: 0x7fff1aa914e0
总结:
不管我们刚刚在示例#1中发现了什么,这个示例都有效!WTF BS!StackOverflow说#1是正确的,为什么你应该使用新的/动态内存来返回指针,但#2是有效的,为什么?
1) testTestObject1()在函数中生成一个TestObject
2) 然后通过引用返回(??)它应该是通过值返回的,但如果你看,地址是相同的?为什么?
3) testTestObject2()在函数中生成一个TestObject
4) 然后返回地址作为指针
5) 但是变量和对象仍然存在,即使它应该消失,因为函数是gone
简而言之:
有人能向我解释为什么在函数中生成的对象即使在函数完成后仍然存在吗?
为什么C++程序员应该尽量减少';新';?
何时使用";新的";在C++中,什么时候不这样做?
我应该什么时候在C++中使用新关键字?
第二种情况和第一种情况一样糟糕,您返回了一个本地构建的堆栈对象的地址。在这种情况下,任何合适的编译器都应该发出警告,并具有适当的警告级别(在g++中使用-Wall -Wextra
来确保捕获到这类东西)。
我的系统上的示例(除了由于定义不完整导致的链接器错误,甚至没有警告标志):
第24行:
warning: address of local variable 'testObject' returned
并住在这里http://ideone.com/mc1ueG
如果你的程序编译了并不意味着你的程序"工作"。它只会导致未定义行为(UB),这是C/C++程序中最变态的错误,因为有时很难诊断。为什么在C/C++中使用UB?因为你不想为你不使用的东西付费,而且编译器也不会对某些语句强制执行严格的检查(尽管大多数时候会发出警告)。这样的验证需要时间,而且,你不希望它们自动强制执行。
您的简单示例之所以有效,是因为您的代码很简单-您的对象使用的堆栈内存仍然存在,而且因为您还没有从main()
返回,所以没有对其进行任何修改。在对类方法的连续调用中,您的本地对象甚至在完全相同的位置具有完全相同的数据。
std::string
不是一个简单的对象。在任何OS/C++运行时上,都很可能进行大量的动态内存分配,这样,如果在调用对象的析构函数后引用对象,则进程将失败。
正如其他人已经指出的那样——你表现得很惊讶,一些未定义的东西在不同的情况下会有不同的效果。
如前所述,您很幸运没有得到未定义的行为,因为您的内存没有被其他程序使用,但如果我们使用新的运算符位置强制程序使用您从函数返回的指针,会发生什么?
TestObject* testTestObject2()
{
/*static*/ TestObject testObject;
std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT):"<<&testObject<<std::endl;
std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
return &testObject;
}
TestObject* tObject2 = testTestObject2();
TestObject * tObject3 = new (tObject2) TestObject;
std::cout << tObject2->getTestVariable() << std::endl;
输出:
--->在这里,编译器不能保护你的内存"未定义的行为",因为我使用new将数据放在了同一个指针中:
(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7ffffddcb0b8
(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x7ffffddcafe8
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
-35868696 (undefined behavior)
(IN MAIN) TESTOBJECT POINTER: 0x7ffffddcafe8
让我们看看我们是否通过静态关键字保护您的返回指针:
TestObject* testTestObject2()
{
static TestObject testObject;
std::cout<<"(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): "<<&testObject<<std::endl;
std::cout<<"(INSIDE FUNCTION) TESTOBJECT.VARIABLE: "<<testObject.getTestVariable()<<std::endl;
return &testObject;
}
输出:在这里,程序使用不同的地址来存储来自返回值的数据
(IN MAIN) ADDRESS OF (TESTOBJECT): 0x7fff479bd8c8
(INSIDE FUNCTION) RETURN (ADDRESS OF TESTOBJECT): 0x6021dc
(INSIDE FUNCTION) TESTOBJECT.VARIABLE: 9999
9999 (data is intact)
(IN MAIN) TESTOBJECT POINTER: 0x6021dc
- Arduino C++在构造函数中用参数声明对象数组
- 为什么我在声明对象数组时不能使用 -> 运算符?
- 如何在 if 语句中声明对象并在任何我想的地方使用它?
- 如何使用unique_ptr和make_unique正确声明对象数组
- 声明对象时,如何在C 中修复C2065错误
- 声明对象而不调用默认构造函数
- 一旦对象设置为 null,用于声明对象的堆大小的变量就设置为 null?
- 我可以使用名称而不是类型来声明对象文字吗?
- 在制作(Qt等)GUI时声明对象
- 在基本的GUI编程中,在哪里声明对象和方法
- 同时使用模板和参数声明对象
- "Undefined reference to"使用双模板类声明对象时出错
- 在头文件 C++ 中声明对象
- 在其类中声明对象
- C 可以通过使用要求其为某个类别的对象来声明对象
- 声明对象
- (C++)如何基于条件声明对象类成员
- 使用变量声明对象的名称
- C++ OOP 体系结构:决定是从基类声明对象还是继承基类
- 在声明对象之前在全局函数中使用类的成员函数