下面的例子,ListNode temp = dummy是引用语义(Java),但是我开始理解成了值语义(C/C++)
class Solution { //c++代码
public:
ListNode* swapPairs(ListNode* head) {
ListNode dummy(0);
dummy.next = head;
ListNode* temp = &dummy; //temp只是指向dummy的一个指针
while(temp->next!=nullptr && temp->next->next !=nullptr){
ListNode* start = temp->next;
ListNode* end = temp->next->next;
temp->next = end; //在这里它只是访问了当前指向的(dummy)节点的next指针,更改了它指向的地址
start->next = end->next;
end->next = start;
temp = start; //temp改变了它指向的对象(不再指向dummy),不是更改了dummy本身
}
return dummy.next;
//return dummy->next;
}
};
class Solution { //Java代码
public ListNode swapPairs(ListNode head) {
ListNode pre = new ListNode(0);
pre.next = head;
ListNode temp = pre; //刚开始一直以为temp是pre的一个拷贝不共享底层数据,是一个"深拷贝“
while(temp.next != null && temp.next.next != null) {
ListNode start = temp.next;
ListNode end = temp.next.next;
temp.next = end;
start.next = end.next;
end.next = start;
temp = start;
}
return pre.next;
}
}
参考:https://www.cnblogs.com/Braveliu/p/3285908.html
- 什么是值语义?
所谓值语义是指目标对象由源对象拷贝生成,且生成后与源对象完全无关,彼此独立存在,改变互不影响。就像 int 类型变量相互拷贝一样。
C++的内置类型(bool/int/double/char)都是值语义,标准库里的 complex<>、pair<>、vector<>、map<>、string 等等类型也都是值语义。
拷贝之后就与源对象完全脱离关系。
- 什么是对象语义?
对象语义也叫指针语义,引用语义等。
通常是指一个目标对象由源对象拷贝生成,但生成后与源对象之间依然共享底层资源,对任何一个的改变都将随之改变另一个。
就像包含有指针成员变量的自定义类在默认拷贝构造函数下对其对象之间进行的拷贝。拷贝后目标对象和源对象的指针成员变量仍指向同一块内存数据。
如果当其中一个被析构掉后,另一个对象的指针成员就会沦为名副其实的悬垂指针!
又比如,Thread 是对象语义,拷贝 Thread 是无意义的,也是被禁止的:因为 Thread 代表线程,拷贝一个Thread对象并不能让系统增加一个一模一样的线程。
- 两者之间的联系
“值” 与 “对象”类型之间并没有严格定义的区分。但通常可以观察到下列不同:
值 是 死的、 傻的、 简单的、 具体的、 可复制的
对象 是 活的、 聪明的、 复杂的、 抽象的、 不可复制的
这里的“复杂性”主要还是指行为的复杂性,而非结构的复杂性。例如,
list< map< vector, deque< set > > >
仍然是一个不折不扣的“值”类型。
值语义的一个巨大好处是生命期管理很简单,比如 int 类型一样——你不需要操心 int 对象 的生命期。
值语义的对象要么是栈对象,或者直接作为其它对象的数据成员,因此我们不用担心它的生命期(一个函数使用自己栈上的对象,一个成员函数使用自己的数据成员变量)。
相反,对象语义的 对象由于不能拷贝,我们只能通过指针或引用来使用它。
一旦使用指针和引用来操作对象,那么就要担心所指的对象是否已被释放,这一度是C++程序bug的一大来源。
此外,由于C++只能通过指针或引用来获得多态性,那么在C++里从事基于继承和多态的面向对象编程有其本质的困难——内存资源管理。
深拷贝与浅拷贝
关于深拷贝与浅拷贝,当class的成员变量中有了指针成员函数。做拷贝构造后,两个指针指向的底层数据是相同的。有可能造成重复释放,从而造成未定义的行为。(浅拷贝)
理解了值语义与引用语义,对于c++自定义类型的生命周期和智能指针都有了更深的了解。