1 题目和参考链接
2 分析
初始化类有两种方式:函数名称均为类名
- 构造函数:参数是成员变量的输入值,没有返回值,作用是输入成员变量的初始值,赋值给成员变量,这是用变量值初始化类;
- 拷贝构造函数:参数是一个类,就是就是我们当前这个类,它的作用是用参数给的这个类的成员变量复制赋值给当前类的成员变量。拷贝又分为深拷贝、浅拷贝、赋值。
浅拷贝、深拷贝、赋值的区别
2.1 数据存储
数据分为:
- 基本数据类型(String, Number, Boolean, Null, Undefined,Symbol):真实的数据直接存储在栈(stack)中
- 对象数据类型(array、object):存储的是该对象在栈中引用,真实的数据存放在堆内存里。
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
2.2 区别
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的。
深拷贝和浅拷贝的示意图大致如下:
- 浅拷贝:会创建一个新对象,只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。
- 深拷贝:会创造一个一模一样的新对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
- 赋值:赋的其实是在栈中的地址,而不是堆中的数据。指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。
总结:
翻译一下:
普通数据类型的数据存在栈里,其他数据类型的是数据存储在堆中,栈中存储的是该数组在堆中地址。
- 针对普通数据来说:
- 赋值:指向栈中的数据
- 浅拷贝和深拷贝没区别:新new一个对象,并将栈中数据拷贝一份赋值给新对象
- 针对object array数据类型来说:
- 浅拷贝:新new一个对象,只复制指向某个对象的指针。
- 深拷贝:新new一个对象,将堆中的数据拷贝一份,新对象指向新开辟的内存。
2.3 实现
如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。拷贝构造函数的最常见形式如下:
classname (const classname &obj) {
// 构造函数的主体
}
在这里,obj 是一个对象引用,该对象是用于初始化另一个对象的。
浅拷贝:直接赋值
Person(const Person &p){//拷贝构造函数
this->name=p.name;
this->age=p.age;
}
深拷贝:先开辟新空间,再赋值
Person(const Person& p){//拷贝构造函数 注意这里使用const防止被修改
this->name=new char[strlen(p.name)+1];
strcpy(this->name,p.name);
this->age=p.age;
}
总结
- 如果不想对象之间相互影响,一定要写深拷贝。
- 串引用的时候,规范写法,记得写
const
,避免无意识的修改不应修改的值。 - 开辟新空间的写法
name = new type [size];
3 题目AC代码
#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;
class Person {
public:
char* name; // 姓名
int age; // 年龄
Person(const char* name, int age) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->age = age;
}
// write your code here......
Person(const Person& p){//拷贝构造函数 注意这里使用const防止被修改
this->name=new char[strlen(p.name)+1];
strcpy(this->name,p.name);
this->age=p.age;
}
void showPerson() {
cout << name << " " << age << endl;
}
~Person() {
if (name != nullptr) {
delete[] name;
name = nullptr;
}
}
};
int main() {
char name[100] = { 0 };
int age;
cin >> name;
cin >> age;
Person p1(name, age);
Person p2 = p1;
p2.showPerson();
return 0;
}
4 验证深浅拷贝的code
- 创建对象
p1
,并赋值 - 拷贝
p1
给p2
,输出p2
- 修改
p1
,输出p2
。(看p2会不会因为p1变了而变化)
#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;
class Person {
public:
char* name; // 姓名
int age; // 年龄
Person(const char* name, int age) {
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
this->age = age;
}
// write your code here......
Person(const Person &p){//拷贝构造函数
//浅拷贝
// this->name=p.name;
// this->age=p.age;
//深拷贝
this->name = new char[strlen(p.name) + 1];
strcpy(this->name, p.name);
this->age=p.age;
}
void showPerson() {
cout << name << " " << age << endl;
}
~Person() {
if (name != nullptr) {
delete[] name;
name = nullptr;
}
}
};
int main() {
char name[100] = "1";
int age=1;
Person p1(name, age);
Person p2 = p1;//拷贝
p2.showPerson();//输出
p1.name[0]='2';//修改P1
p1.age=2;
p2.showPerson();//输出
return 0;
}
浅拷贝:
深拷贝:
5 练习-数组类的深拷贝函数
class Array{
private:
int n;//数组大小
int *a;//数组
public:
Array(){
cin>>n;
a=new int [n];
for (int i=0;i<n;i++) cin>>a[i];
}
//my code
Array(const Array &b){
this->n=b.n;
this->a=new int[this->n];//申请空间
for(int i=0;i<this->n;i++){
this->a[i]=b.a[i];
}
}
//.....