首先,在C++中类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立,如A* p=new A(),Ap=(A)malloc();静态建立一个类对象,是由编译器为对象在栈空间中分配内存,通过直接移动栈顶指针挪出适当的空间,然后在这片内存空间上调用构造函数形成一个栈对象。动态建立类对象,是使用new运算符将对象建立在堆空间中,在栈中只保留了指向该对象的指针。栈是由编译器自动分配释放 ,存放函数的参数值,局部变量的值,对象的引用地址等。其操作方式类似于数据结构中的栈,通常都是被调用时处于存储空间中,调用完毕立即释放。堆中通常保存程序运行时动态创建的对象,C++堆中存放的对象需要由程序员分配释放,它存在程序运行的整个生命期,直到程序结束由OS释放。而在java中通常类的对象都分配在堆中,对象的回收由虚拟机的GC垃圾回收机制决定。
1.下面的程序来看看静态建立和动态建立对象的区别
#include<iostream>
#include<string>
using namespace std;
class student
{
public:
string name;
int age;
void sayhello();
};
void student::sayhello()
{
cout<<"my name is: "+this->name+" I am: "<<this->age;
cout<<"\n";
}
student setname(string name)
{
student stu;
stu.age=12;
stu.name=name;
return stu;
}
int main()
{
student stu=setname("jim");
stu.sayhello();
return 0;
}
程序运行结果:my name is: jim I am: 12;
程序定义了一个student类,在setname函数中定义一个局部对象作为返回值。程序第18行静态构建了一个student对象stu,它在栈上分配空间,在函数调用结束后就销毁了,函数返回的类对应在内存中的值应该不存在啊?其实原来C++在用类作为函数的返回值时调用了类的拷贝构造函数,而且该拷贝构造函数是在堆上分配存储空间,后面再讨论这个问题。
在setname函数内的stu在函数调用结束后就销毁了,可以添加一个析构函数来证明:
在student类中加入析构函数:
student::~student()
{
cout<<this->name<<":gameover"<<endl;
}
程序运行结果:
在sayhello()前,输出jim:gameover,即为setname()里的stu对象执行了析构函数。
如将setname函数改为:
student* setname(string name)
{
student stu;
stu.age=12;
stu.name=name;
return &stu;
}
main函数的调用改为:
int main()
{
student* p=setname("tom");
p->sayhello();
return 0;
}
显然这里会出现问题,对象指针返回的是栈上的对象,在函数调用结束后已经销毁了,对象指针即为野指针,故程序在编译时会提示:warning C4172: returning address of local variable or temporary。解决这个问题我们自然想到把该对象构建在堆上即可。修改setname函数为下:
student* setname(string name)
{
student* stu= new student();
stu->age=12;
stu->name=name;
return stu;
}
main函数的调用不变;程序正常运行输出:
上面输出结果并没有调用析构函数,在setname调用后,在main函数结束后也没有调用。在对上的对象需要程序员自己delete释放,将main改为如下:
int main()
{
student* p=setname("tom");
p->sayhello();
delete p;
return 0;
}