我正在从Java转向c ++,而且当我学习如何在C ++中完成工作时,我有时会感到困惑。我在网上读到,如果在函数内部创建了一个对象,它只存在于函数内部,除非它是使用new声明的。所以我编写了以下代码来测试它:
#include <iostream>
using namespace std;
class Student{
private:
int ID;
int score;
public:
void setID(int num);
int getID();
void setScore(int num);
int getScore();
};
void Student::setID(int num)
{
ID = num;
}
int Student::getID()
{
return ID;
}
void Student::setScore(int num)
{
score = num;
}
int Student::getScore()
{
return score;
}
class Creator
{
public:
static int nextID;
Student getObject();
};
int Creator::nextID = 0;
Student Creator::getObject()
{
Creator::nextID++;
Student temp;
temp.setID(Creator::nextID);
return temp;
}
int main()
{
Creator maker;
Student pupil[4];
for(std::size_t i = 0; i < (sizeof(pupil)/sizeof(pupil[0])); i++)
{
pupil[i] = maker.getObject();
}
for(std::size_t i = 0; i < (sizeof(pupil)/sizeof(pupil[0])); i++)
{
cout<< "Sudent ID: "<<pupil[i].getID()<<endl;
}
int mark = 70;
for(std::size_t i = 0; i < (sizeof(pupil)/sizeof(pupil[0])); i++)
{
pupil[i].setScore(mark);
mark += 10;
}
for(std::size_t i = 0; i < (sizeof(pupil)/sizeof(pupil[0])); i++)
{
cout<< "Sudent ID: "<<pupil[i].getID()<<" has score of: "<<pupil[i].getScore()<<endl;
}
return 0;
}
该程序按预期工作,这让我感到困惑。根据我读到的内容,Student Creator::getObject()
内部创建的对象不应该存在于其之外。它随着函数的返回而被破坏。然而,我正在返回Student Creator::getObject()
内部创建的对象并将其存储在Student Creator::getObject()
之外的pupil数组中。
既然它有效,它是否意味着对象是在堆上创建的?根据我的内容,如果在函数内部创建对象并且未使用new关键字,则会在堆栈中创建对象并在函数退出时销毁该对象。
答案 0 :(得分:2)
关于这个功能:
Student Creator::getObject()
{
Creator::nextID++;
Student temp;
temp.setID(Creator::nextID);
return temp;
}
这实际上是使用类'复制构造函数创建返回值的 COPY 。确实从堆栈中删除了temp
变量。
你不能做什么,是这样的:
Student& Creator::getObject()
{
Creator::nextID++;
Student temp;
temp.setID(Creator::nextID);
return temp; // <- trying to return a reference transient memory
}
答案 1 :(得分:1)
既然它有效,它是否意味着对象是在堆上创建的?
没有。除非您致电new Student()
从我读到的内容来看,如果对象是在函数内部创建的,并且没有使用new关键字,则会在堆栈中创建对象并在函数退出时销毁该对象。
这基本上是正确的,尽管你可以返回该对象的副本作为函数的返回值,并让编译器决定return value optimization。
答案 2 :(得分:0)
查看getObject的签名
Student Creator::getObject()
getObject返回您返回的任何内容的副本。确实,一旦你的函数退出,temp
将被销毁,但是你没有返回对temp的引用,你将返回temp的值。
答案 3 :(得分:0)
在Java中,AFAIK在堆上创建所有对象,Java引用为您计算对象。在C ++中,如果你实例化一个对象,那么你是正确的,它是在堆栈上创建的,除非你或你调用的函数使用new。所以:
学生; //在堆栈上
学生* sp =新学生; //在堆上
学生&sr = *新生; //在堆上
在C ++中,堆栈中的所有对象在超出范围时都会被销毁,因此您的对象temp被销毁。在上面的代码中,当sp和sr本身超出范围时,sp和sr指向/引用的对象仍然存在。发生的情况是浅拷贝(默认情况下)是由temp组成的,这是返回的,然后清除temp本身。这与您返回引用的Java不同。实际上,C ++正在调用copy-constructor来制作temp的副本,并且通过为Student编写自己的copy-ctor,如果你的类有指针或参考,你就可以复制这些对象,而不仅仅是复制指针/ reference,这是默认的浅拷贝所发生的。
答案 4 :(得分:0)
temp
被创建为局部变量,因此其范围确实与函数绑定。 temp
在getObject
结束时咬了一个大的。
但是getObject
会返回Student
,而不是Student
的引用或指针。在这一点上,编译器的一时兴起可能会发生各种各样的酷事。可以复制temp
,但由于temp
永远不会再使用getObject
,因此也可以移动temp
。在某些情况下,可以省略// the button has to be somehow initialized and maybe added to layout
QPushButton* pMyPushButton = new QPushButton(this);
layout()->AddWidget(pMyPushButton);
// try style it like that
// the concrete example limits the scope to pMyPushButton object
pMyPushButton->setStyleSheet(
"QPushButton {"
"background: transparent;"
"image: url(res/images/normal_bn_image.png);"
"}"
"QPushButton:hover {"
"image: url(res/images/hover_bn_image.png);"
"}"
);
。编译器会做任何它认为最好的选择。
最好的部分是你可能甚至不会照顾。