[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

这篇具有很好参考价值的文章主要介绍了[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面:Python和C++中的赋值与深浅拷贝,由于其各自语言特性的问题,在概念和实现上稍微有点差异,本文将这C++和Python中的拷贝与赋值放到一起,希望通过对比学习两语言实现上的异同点,加深对概念的理解。

1. Python中的赋值、浅拷贝、深拷贝

C++中所谓的 浅拷贝就是由(系统默认的)拷贝构造函数对数据成员进行逐一的赋值 ,通常默认的拷贝构造函数就是可以达到该效果的,但是如果类中有指针类型的数据(需要在堆上分配内存),那么此时使用默认的拷贝构造函数就会带来错误。因为此时采用简单的浅拷贝,则两个类中的两个指针将指向同一个地址,当对象释放时,会调用两次析构函数,而导致指针悬挂现象(悬浮指针)

而C++的 深拷贝则是,使用自定义的拷贝构造函数,将原有对象的所有成员变量拷贝给新对象,对于指针等数据还会为新对象重新在堆上分配一块内存,并将原有对象所持有的堆上的数据也拷贝过来,这样能保证原有对象和新对象所持有的动态内存都是相互独立的,更改一个对象的数据不会影响另一个对象,同时也不会造成double free的错误。

C++中的 赋值,默认调用的是默认的拷贝构造函数即浅拷贝,如果要使用深拷贝需要重载赋值运算符,为动态内存在堆上分配空间即可~

C++ 浅拷贝示例:

  #include <iostream>

  // 浅拷贝 使用默认的构造函数
  class shallowCopy {
  public:
      shallowCopy(int len) : m_len(len) {
          m_ptr = new int(0); // m_ptr指向一个值为0的int
      }
      shallowCopy() {}

      ~shallowCopy() {
          delete m_ptr;
      }

  public: // 定义为public,方便输出
      int* m_ptr;
      int m_len;
  };

int main()
{
  shallowCopy sc(1);
  auto sc1 = sc; // 浅拷贝
  std::cout << "shallowCopy: " << std::endl;
  std::cout << "sc.m_ptr = " << sc.m_ptr << std::endl;
  std::cout << "sc1.m_ptr = " << sc1.m_ptr << std::endl;  
}

>>>shallowCopy: 
sc.m_ptr = 0x560c930aeeb0
sc1.m_ptr = 0x560c930aeeb0
free(): double free detected in tcache 2  // 尝试两次释放同一地址!!!报错
Aborted

C++ 深拷贝示例:

#include <iostream>

  
class deepCopy {
  public:
      deepCopy(int len) : m_len(len) {
          std::cout << "call deepCopy(int len) " << std::endl;
          m_ptr = new int(1);
      }
      deepCopy(const deepCopy& deepcopy) {
          std::cout << "call deepCopy(const deepCopy& deepcopy) " << std::endl;
          m_len = deepcopy.m_len;
          m_ptr = new int(*(deepcopy.m_ptr)); // 重新分配内存,并且赋值
      } // 拷贝构造函数
      ~deepCopy() {
          delete m_ptr;
      }

  public:
      int* m_ptr;
      int m_len;
 };

int main()
{	
  std::cout << "deepCopy: " << std::endl;
  deepCopy dc(1);
  deepCopy dc1(dc); // 深拷贝
  std::cout << "dc.m_ptr = " << dc.m_ptr << std::endl;
  std::cout << "dc1.m_ptr = " << dc1.m_ptr << std::endl;    
}

>>>deepCopy: 
call deepCopy(int len) 
call deepCopy(const deepCopy& deepcopy) 
dc.m_ptr = 0x560c930af2e0
dc1.m_ptr = 0x560c930af300

2. C++中的赋值、浅拷贝、深拷贝

在Python参数传递,“值传递”还是“引用传递“?一文中我们从Python中可变对象与不可变对象的角度理解了Python中的参数传递的方式,在赋值、深拷贝、浅拷贝中,我们同样从这个角度入手,理解Python中的深浅拷贝。对可变对象、不可变对象不是很清晰的同学,可以移步链接复习一下~。

  • 不可变对象:一旦创建就不可修改的对象,包括字符串、元组、数值类型

(该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。)

  • 可变对象:可以修改的对象,包括列表、字典、集合

(该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的地址,通俗点说就是原地改变。)


2.1 概念

  1. 赋值,类似于C++中的引用(别名),只是复制了新对象的引用,不会开辟新的内存空间,Python中赋值的一般形式为a = 'nihao',内存中实现是:内存开辟空间存储字符串nihao,将a指向这块内存空间:

[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝,开发语言之C++ &amp; Python,c++,开发语言

  1. 浅拷贝: 创建新对象,其内容是原对象的引用。

​ Python中的浅拷贝有三种形式: 切片操作,工厂函数,copy模块中的copy函数。

​ 如: lst = [1,2,[3,4]]

切片操作lst1 = lst[:] 或者 lst1 = [each for each in lst]

工厂函数:lst1 = list(lst)

copy函数:lst1 = copy.copy(lst)

​ 浅拷贝之所以称为浅拷贝,是因为它仅仅只拷贝了一层,拷贝了最外围的对象本身,内部的元素都只是拷贝了一个引用而已,如在lst中有一个嵌套的 list[3,4],如果我们修改了它,情况就不一样了。

​ 浅拷贝要分两种情况进行讨论:

​ 1)当浅拷贝的值是 不可变对象(字符串、元组、数值类型) 时和“赋值”的情况一样,对象的id值 (id()函数用于获取对象的内存地址) 与浅拷贝原来的id值相同。

​ 2)当浅拷贝的值是 可变对象(列表、字典、集合) 时会产生一个“不是那么独立的对象”存在。

​ 2.1) 拷贝的可变对象中无复杂子对象,原来值的改变并不会影响浅拷贝的值,同时浅拷贝的值改变也并不会影响原来的值。

​ 2.2) 拷贝的可变对象中有复杂子对象(例如列表中的一个子元素是一个列表),如果不改变其中复杂子对象,浅拷贝的值改变并不会影响原来的值。 但是改变原来的值中的复杂子对象的值会影响浅拷贝的值。

  1. 深拷贝:和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。

只有一种形式,copy模块中的deepcopy函数

2.2 示例:从例子中理解

1) 不可变对象的赋值、深拷贝、浅拷贝
import copy

# 不可变对象,无法添加删除元素
a = (1, 2, 3)

print("==========")
b = a
print(a, b)
print(id(a), id(b))

print("=====shallow copy=====")
s = copy.copy(a)
print(a, s)
print(id(a), id(s))

print("=====deep copy=====")
d = copy.deepcopy(a)
print(a, d)
print(id(a), id(d))

>>>==========
((1, 2, 3), (1, 2, 3))
(4564433008, 4564433008)
=====shallow copy=====
((1, 2, 3), (1, 2, 3))
(4564433008, 4564433008)
=====deep copy=====
((1, 2, 3), (1, 2, 3))
(4564433008, 4564433008)
2) 可变对象的赋值、浅拷贝与深拷贝
import copy

a = [1, 2, 3]
print("==========")
b = a
b.append(4)
print(a, b)
print(id(a), id(b)) # 赋值仅是变量的别名,两变量拥有相同的内存地址,无论更改哪一个另一个都会更改

a = [1, 2, 3]
print("=====shallow copy=====")
s = copy.copy(a)
print(a, s)
print(id(a), id(s))
a.append(4)
print("------append 4-------")
print(a, s)
print(id(a), id(s))

a = [1, 2, 3]
print("=====deep copy=====")
d = copy.deepcopy(a)
print(a, d)
print(id(a), id(d))
print("------append 4-------")
a.append(4)
print(a, d)
print(id(a), id(d))


>>>==========
([1, 2, 3, 4], [1, 2, 3, 4])
(4564157144, 4564157144)
=====shallow copy=====
([1, 2, 3], [1, 2, 3])
(4564158440, 4564158512)
------append 4-------
([1, 2, 3, 4], [1, 2, 3])
(4564158440, 4564158512)
=====deep copy=====
([1, 2, 3], [1, 2, 3])
(4564158368, 4564158440)
------append 4-------
([1, 2, 3, 4], [1, 2, 3])
(4564158368, 4564158440)
3) 可变对象深浅拷贝(外层、内层改变元素)

[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝,开发语言之C++ &amp; Python,c++,开发语言

# 外层元素更改
import copy
l = [1, 2, 3, [4, 5]]

l1 = l
l2 = copy.copy(l)
l3 = copy.deepcopy(l)
l.append(6) 

print(l)
print(l1)
print(l2)
print(l3)

>>>[1, 2, 3, [4, 5], 6]
[1, 2, 3, [4, 5], 6]
[1, 2, 3, [4, 5]]
[1, 2, 3, [4, 5]]

# 内层元素更改
import copy
l = [1,2,3,[4, 5]]

l1 = l #赋值
l2 = copy.copy(l) #浅拷贝
l3 = copy.deepcopy(l) #深拷贝
l[3].append(6) 

print(l) 
print(l1)
print(l2)
print(l3)

>>> [1, 2, 3, [4, 5, 6]] 
[1, 2, 3, [4, 5, 6]] 
[1, 2, 3, [4, 5, 6]] 
[1, 2, 3, [4, 5]]
  1. 外层添加元素时,浅拷贝不会随原列表变化而变化;内层添加元素时,浅拷贝才会变化。

  2. 无论原列表如何变化,深拷贝都保持不变。

  3. 赋值对象随着原列表一起变化。文章来源地址https://www.toymoban.com/news/detail-785412.html

到了这里,关于[开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java中的浅拷贝与深拷贝

    在Java中,对象的拷贝有两种方式:浅拷贝和深拷贝。它们分别代表了不同的拷贝方式,拷贝出的新对象与原始对象之间存在一定的差异。本文将详细介绍浅拷贝和深拷贝的概念、特点和实现方式,并且通过实例进行解析。  一、浅拷贝  浅拷贝是指在对一个对象进行拷贝时,

    2024年02月14日
    浏览(25)
  • C#中的浅拷贝与深拷贝

    众所周知,C#中有两种类型变量:那就是 值类型 和 引用类型 。对于值类型而言,copy就相当于是全盘复制了,真正的实现了复制,属于 深拷贝 ;而对于引用类型而言,一般的copy只是 浅拷贝 ,只是copy到了引用对象的地址,相当于值传递了一个引用指针, 新的对象通过地址

    2024年02月11日
    浏览(33)
  • 详解js中的浅拷贝与深拷贝

    1.1 栈(stack)和堆(heap) 栈(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈; 堆(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表 1.2 基本数据类型和引用数据类

    2024年02月08日
    浏览(75)
  • Pandas数据中的浅拷贝与深拷贝

    pandas库主要有两种数据结构DataFrames和Series。这些数据结构在内部用索引数组和数据数组表示,索引数组标记数据,数据数组包含实际数据。现在,当我们试图复制这些数据结构(DataFrames和Series)时,我们实际上是复制对象的索引和数据,有两种方法可以做到这一点,即浅复

    2024年02月09日
    浏览(30)
  • [开发语言][python][c++]:C++中的this指针和Python中的Self -- 26岁生日

    以朋友的新岁祝福开篇,祝笔者也祝大家☺️: 之前一直对 C++ 中的 this 和 python 中的 self 到底是什么关系,为什么 C++ 要显式的写出来,python 则不需要? 模糊不清,趁着周末整理一下相关结论,希望本篇文章可以解答这些问题,同时对C++和Python中的类加深些理解。 python 当

    2024年01月24日
    浏览(48)
  • 详解python列表等对象的赋值和复制(浅拷贝copy()及深拷贝deepcopy()的使用区别与示例)

    python虽然没有指针的概念,但是对象、引用、地址这些内容还是存在的,尤其是像列表对象、某类型对象的引用上,搞清楚对象变量的复制和赋值很有必要,不然容易出现“莫名其妙”的错误。 python中给一个变量 a 赋值列表实际上是创建了一个列表对象,并将该列表的地址赋

    2024年02月04日
    浏览(34)
  • C++的引用 拷贝赋值和引用赋值

        💯 博客内容:C++的引用 拷贝赋值和引用赋值 😀 作  者:陈大大陈 🚀 个人简介:一个正在努力学技术的准前端,专注基础和实战分享 ,欢迎私信! 💖 欢迎大家:这里是CSDN,我总结知识和写笔记的地方,喜欢的话请三连,有问题请私信 😘 😘 😘 目录 引用  引用

    2024年02月12日
    浏览(28)
  • c++拷贝构造与赋值运算符重载

    目录 目录:         1:拷贝构造         2:赋值运算符重载                  前言:在上一章我们已经学习过了,构造与析构这两个默认成员函数了,接下来让我们一起来学习另外两个重要的默认成员函数。         首先让我们来讲一下默认成员函数这个概念,所谓

    2024年02月08日
    浏览(40)
  • 【C++】vector类的模拟实现(增删查改,拷贝构造,赋值运算,深浅拷贝)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 我们模拟vector是用迭代器(start,end,endofstorage)来控制增删查改操作的 1普通构造 2迭代器构造 3初始化字符构造 4拷贝构造: 1当前大小(size()) 2总体容量(capacity()) 1begin()

    2024年02月15日
    浏览(45)
  • 【C++】:拷贝构造函数和赋值运算符重载

    拷贝构造函数是特殊的构造函数。 是用一个已经存在的对象,赋值拷贝给另一个新创建的已经存在的对象 。 本质:用同类型的对象拷贝初始化 。 拷贝构造函数也是 特殊的成员函数 ,其特征如下: 2.1 拷贝构造函数是构造函数的一个重载形式。 2.2 拷贝构造函数的 函数名域

    2024年04月28日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包