1、首先来说一下java中引用类型对象是怎么赋值:
java中对于引用类型的对象的赋值(即用“=”把他们两种相连)都是指针传递,即复制一个指针给另外一个对象,这两个对象大家都指向同一处堆内存空间(如果这个对象是new的),即同一块地址。所以当前一个对象赋值为null后,后面那个对象同样可以指向之前的内容,丝毫没有影响。这个相当于c++对象之间的浅拷贝,即把指针也一块拷贝过去了。
下面看一个简单的java的demo
package indi.demo;
public class Test {
public static void main(String[] args) {
/*总结:java中对于引用类型的对象都是指针传递,即复制一个指针给另外一个对象,大家都指向同一处堆内存空间。
而c++若成员变量中含有指针的对象的赋值和“=”,是真正的复制,也就是说只是复制的内容,涉及到开辟内存空间的是就新开辟一个内存空间
来存储复制的内容,当然地址(指针)也就发生了变化,但是指向的内容相等,不能说相同,因为存储相等东西的存储地址变了。
为什么C++不像java那样呢,因为完全没必要啊,若真的想指向同一个内容(内存区域),完全传递指针或者引用就可以了啊,java中它是没指针的。
*/
Student s1=new Student("zhangsan","001",new Student("呵呵","002", null));
Student s2=null;
s2=s1;
s2.getStu().setName("呵呵1");
System.out.println(s1.getStu().getName());
s1=null;
System.out.println(s2.getStu().getName()+"s2");
}
}
2、再来了解下C++中成员变量含有指针的对象间的赋值:
c++中成员变量含有指针的对象的赋值(即用“=”把他们相连),是真正的复制,也就是说只是复制的内容,涉及到需要开辟内存空间的就新开辟一个内存空间来存储复制的内容(这个需要在拷贝构造函数,或者=号操作符重载中进行操作,当然也需要在析构函数中delete掉该对象中自己新开辟的堆内存空间),因此地址(指针)也就发生了变化,但是指向的内容相等,不能说相同,因为存储相等东西的存储地址变了(指针里面就是存的该存储空间的首地址)。
问题:为什么C++不能像java那样直接赋值呢:两个同类对象间直接进行引用传递。答案是:如果对象内不含指针,也就是类中包含的数据元素全部在栈上,没在堆上,浅拷贝也是完全可以的,对象将完全和java一样直接赋值,和java唯一的区别是这个对象是在栈上,而java中new的对象是在堆上。但是,如果对象内部含有指针,也就是类中的数据元素有在堆内存上的,就不能像java那样只是用"="简单赋值了,还应该建立相应的拷贝构造函数和"='等号操作符重载函数,为存放在堆内存上的数据重新开辟空间,防止析构的时候重复删除对应指针出的内容,导致宕机。java它有自己的垃圾回收机制,会自动清理堆内存中的垃圾,而c++中一般是通过析构函数中delete或者手动delete。凡是指向堆内存的指针都不能delete掉两次,不然程序都会出错。所以c++中的“=”分两种情况,都在栈上时就是指针传递,含有堆内存数据时就是复制,java中除基本类型外都是指针传递。这儿需要明确一点,c++中通过new创建的数据都是存放在堆内存的,new返回的都是指向堆内存空间的指针,当然c语言中的malloc函数开辟的空间也是在堆内存上,与new相似,只是new与delete会调用构造和析构函数,所以c++中用new和delete,用new开辟的堆内存空间,一定要手动删除。
这儿额外补充个知识:c++中用new和不用new创建对象的本质区别!
1:作用域不同
不用new:作用域限制在定义类对象的方法中,当方法结束时,类对象也被系统释放了,(安全不会造成内存系统泄漏)。
用new:创建的是指向类对象的指针,作用域变成了全局,当程序结束时,必须用delete删除,系统不会自动释放,(不注意可能造成内存泄漏)。
2:用new 是在堆内存上开辟空间,而不用new就是在栈上,相当于一个局部变量。
下面给出一个c++成员变量含有指针的一个对象的demo:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Student
{
private:
int id;
char *name;
public:
Student() {
id = 0;
name = NULL;
}
//构造函数中,对于成员函数是指针的同样要开辟存储空间
Student(int id, char *name) {
this->id = id;
int len = strlen(name);
this->name = new char[len + 1];
strcpy(this->name, name);
}
//拷贝构造函数
Student(const Student &std) {
this->id = std.id;
//执行深拷贝
int len = strlen(std.name);
this->name = new char[len+1];
//将之前的name对应的字符串拷贝到现在新开辟的内存空间对应的name地址
strcpy(this->name, std.name);
}
//等号操作符重载
Student & operator=(const Student & std) {
//思路:成员变量中含有指针的对象赋值,必须考虑重新开辟一个存储空间,而且开辟之前要把自己指针所对应的空间销毁
//防止出现自身赋值
if (this->name == std.name) {
return *this;
}
//将指针之前对应的内存空间销毁
if (this->name != NULL) {
delete name;
name = NULL;
this->id = 0;
}
//开辟新的内存空间,用于复制想要拷贝的东西
this->id = std.id;
int len = strlen(std.name);
this->name = new char[len+1];
strcpy(name, std.name);
}
//析构函数,必须清理掉自身开辟的内存空间,c++只给你清理本身对象所对应的空间,而你自己开辟的对应指针的空间需自己销毁,
//而java什么都不用你操心
~Student() {
//判断name是否为空,不为空就清理掉它对应的内存空间,并将name赋值为空
if (name != NULL) {
//注意指针对应的若是数组,应该用delete[]!!!
delete[] name;
name = NULL;
}
}
int getId() {
return this->id;
}
const char * getName() {
return this->name;
}
};
int main() {
Student s1(1, "zhang3");
Student s2(2, "li4");
Student s3=s2;
cout << s1.getId() << "," << s1.getName() << endl;
//s3 = s2;
cout << s3.getId() << "," << s3.getName() << endl;
}