C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数
文章目录
- 1. 深拷贝和浅拷贝
- 1.1 缺省拷贝构造函数
- 1.2 空指针的空字符串
- 1.3 怎么判断是否为内核资源?
- 1.4 深拷贝构造函数
- 1.5 为什么使用深拷贝构造函数?
- 2. 值类型
- 2.1 值类型定义
- 2.2 值类型特点
- 2.3 代码示例
- 3. 构造函数和拷贝构造函数
- 3.1 构造函数
- 3.1.1 构造函数用途
- 3.1.2 构造函数类型转换(运算符重载)
- 3.1.3 其他运算符重载
- 3.1.4 成员指针
- 3.1.5 局部对象内存分配图
- 3.1.6 为什么系统要提供缺省构造函数和缺省赋值语句?
- 3.1.7 对象内存分配图
- 3.2 拷贝构造函数
- 3.2.1 何时需要自己定义拷贝构造函数和赋值语句?
- 3.2.2 深拷贝
- 3.2.3 代码示例
- 4. 函数重载
- 4.1C++编程特点
- 4.2 C++调用函数顺序
- 4.3 C++切片问题
- 5. 移动构造函数和移动赋值函数
- 5.1 引用类型
- 5.2 移动构造函数
- 5.3 移动赋值函数
1. 深拷贝和浅拷贝
1.1 缺省拷贝构造函数
1.2 空指针的空字符串
char* p=nullptr;//空指针,没有内存地址空间
p=new char[100];*p='\0';//空字符串——指向地址空间,地址空间存放\0,有内存地址
1.3 怎么判断是否为内核资源?
class FileMan
{FILE* fp;//文件类型指针
public:FileMan(const char* filename):fp(nullptr){fopen_s(&fp, filename, "w");//文件资源不占当前内存,为内核资源}~FileMan(){fclose(fp);fp = nullptr;}
};
1.4 深拷贝构造函数
#include<iostream>
#include<string>
using namespace std;
class MyString
{char* str;
public:MyString(const char* p = nullptr):str(nullptr){if (p != nullptr){int n = strlen(p) + 1;str = new char[n];//堆区strcpy_s(str, n, p);//n=n-1存放\0}else{str = new char[1];*str = '\0';//空字符串}}~MyString() {delete[] str;str = nullptr;}MyString(const MyString& s) :str(nullptr)//拷贝构造函数(深){int n = strlen(s.str) + 1;str = new char[n];strcpy_s(str, n, s.str);}void PrintString() const{cout << str << endl;}
};
1.5 为什么使用深拷贝构造函数?
防止自赋值
2. 值类型
(lvalue左值,xvalue僵王值,rvalue右值,prvalue纯右值)
2.1 值类型定义
- 左值:可以取地址
- 右值:不能取地址,无名对象
- 将亡值:在表达式运行过程中,所产生的无名实体
- 纯右值:字面常量
- 将亡值:在函数运行过程中产生的临时对象
2.2 值类型特点
1.左值和将亡值合称泛左值,纯右值和将亡值合称右值。
2.函数执行时生成将亡值,函数结束将亡值死亡。
3.严格来讲,“左值"是表达式的结果的一种属性,但更为普遍地,我们通常用"左值"来指代左值表达式。所谓左值表达式,就是指求值结果的值类别为左值的表达式。通常我们无需区分"左值"指的是前者还是后者,因为它们表达的是同一个意思,不会引起歧义。在后文中,我们依然用左值指代左值表达式。对于纯右值和将亡值,亦然。
4.右值引用
右值引用只能引用纯右值
int&& c=10;
2.3 代码示例
//右值拷贝构造(优先调用构建将亡值对象)String(String&& s){cout << "move copy construcgt" << this << endl;str = s.str;s.str = NULL;}//右值赋值语句(优先调用构建将亡值对象)String& operator=(String&& s){if (this != &s){str = s.str;s.str = NULL;}cout << this << "move operator=:" << &s << endl;return *this;}//防止内存泄漏char* Release(char* p){char* old = str;str = p;return old;}
class String
{private:char* str;String(char* p, int){str = p;}
public:String(const char* p = NULL) :str(NULL){cout << "construct :" << this << endl;if (p != NULL){str = new char[strlen(p) + 1];strcpy(str, p);}else{ /??str = new char[1]; // str = NULL;*str = '\0';}} // s1.str ~String(){cout << "destroy : " << this << endl;if (str != NULL){delete[] str;}str = NULL;}//ostream& operator<<(const String* const this, ostream& out);ostream& operator<<(ostream &out) const{if (str != NULL){out << str;}return out;}String(const String& s):str(NULL){cout << "copy construct : " << this << endl;str = new char[strlen(s.str + 1)];strcpy(str, s.str);}String operator+(const String& s) const{char* p = new char[strlen(this->str) + strlen(s.str) + 1];strcpy(p, this->str);strcat(p, s.str);return String(p,1);}String operator+(const char* s) const{char* p = new char[strlen(this->str) + strlen(s) + 1];strcpy(p, this->str);strcat(p, s);return String(p, 1);//return *this + String(s);}
#if 1String& operator=(const String& s){if (this != &s){delete[]str;str = new char[strlen(s.str) + 1];strcpy(str, s.str);//delete[]str;//new (this) String(s); // }cout << this << " operator= : " << &s << endl;return *this;}String(String&& s){cout << " move copy construcgt :" << this << endl;str = s.str;s.str = NULL;} // String(s2);String& operator=(String&& s){if (this != &s){s.str = Relese(s.str);}cout << this << " move operator=: " << &s << endl;return *this;}
#endifchar* Relese(char* p){char* old = str;str = p;return old;}// s1 = String();
};// lvalue xvalue rvalue prvalue; //ostream& operator<<(ostream& out, const String& s)
{s << out; return out;
}String operator+(const char* p, const String& s)
{return String(p) + s;
}
int main()
{String s1("yhping");String s2("hello");s1 = std::move(s2);return 0;
}
3. 构造函数和拷贝构造函数
3.1 构造函数
3.1.1 构造函数用途
① 创建对象
② 初始化对象中的属性
③类型转换(可以将内置类型转换为自己设计的类型,从而给变量进行赋值,当构造函数只有一个参数时,才能进行类型转换)
3.1.2 构造函数类型转换(运算符重载)
- (1)将内置类型转换为自己设计的类型
如不允许隐式构造,则加上explicit关键字
explicit关键字详解
#include<iostream>
using namespace std;class Int
{private:int value;
public:explicit Int(int x = 0) :value(x)//explicit关键字{cout << "Create Int:" << this << endl;}Int(const Int& it) :value(it.value){cout << "Copy Create Int" << this << endl;}Int& operator=(const Int& it){if (this != &it){value = it.value;}cout << this << " + " << &it << endl;return *this;}~Int(){cout << "Destroy Int:" << this << endl;}
};
int main()
{Int a(10);int b = 100;a = (Int)b;//强转后赋值return 0;
}
Int(int x,int y = 0) :value(x + y)//explicit关键字{cout << "Create Int:" << this << endl;}//int b=100;//1.a=(Int)(b,20); 20 强转将20赋值给x,y=0,类型转换方式产生,构造函数单参;//2.a=Int(b,20); 120 调用构造函数创建无名对象,将20赋值给y,x=100;
- (2)将对象赋值给内置类型
设计强转类型
注:强转后返回类型为自身类型,所以函数无返回类型
类型强转函数
//重载整型强转运算符,对象强转赋值个变量operator int() const{return value;}
int main()
{Int a(10);int b = 100;a = (Int)b;//强转后赋值b = a;b = a.operator int();//b=operator int(&a);b = (int)a;return 0;
}
3.1.3 其他运算符重载
c++ 关键字 mutable
class Add
{mutable int value;
public:Add(int x=0) :value(x) {}//重载()运算符int operator()(int a, int b) const{value = a + b;return value;}};
int main()
{int a = 10, b = 20, c = 0;Add add;c = add(a, b);//重载()运算符的对象称之为仿函数(add)//c= add.operator()(a,b);return 0;
}
(1)对象调用拷贝构造函数,调动缺省构造函数构建全局对象num和val
(2)进入构造函数函数内部,对num和val进行赋值
(3)调用构造函数创建临时对象,调动赋值语句
(4)调动析构函数,obj对象构建完成,先释放成员对象,然后释放对象本身
构建顺序和参数列表无关
class Object
{int num;//调动缺省构造函数构建int val;
public:Object(int x, int y)//对象调用拷贝构造函数,进入函数之内,{num = x;val = y;}
};
int main()
{Object obj(1, 2);
}
3.1.4 成员指针
class Object
{int* ip;//调动缺省构造函数构建int val;
public:Object(Int*s=NULL) :ip(s)//对象调用拷贝构造函数,进入函数之内,{}Object(){if (ip != NULL){delete ip;}ip = NULL;}
};
int main()
{Object obj(new Int(10));
}
对生存期自动管理(系统自动生成析构函数)
1.从堆区申请空间
2.调用构造函数构建对象
3.返回构建对象地址
例1:Complex类
#include<iostream>
#include<string>
using namespace std;
class Complex
{int Real;int Image;
public:Complex(int r = 0, int i = 0)//构造函数:Real(r), Image(i){cout << "Create Complex: " << this << endl;}Complex(const Complex& cx)//拷贝构造:Real(cx.Real), Image(cx.Image){cout << "Copy Create Complex: " << this << endl;}~Complex(){cout << "Destroy Complex: " << this << endl;}void Print() const{cout << "Real" << Real << "Image" << Image << endl;}//方法一/*Complex Add(const Complex& cx)//创建对象{Complex tmp;tmp.Real = this->Real + cx.Real;tmp.Image = this->Image + cx.Image;return tmp;}*/Complex Add(const Complex& cx) const{int r = this->Real + cx.Real;int i = this->Image + cx.Image;return Complex(r, i);}
};
int main()
{Complex c1(1, 2), c2(3, 4), c3;c3 = c1.Add(c2);c3.Print();return 0;
}
例2:Int类
#include<iostream>
#include<string>
using namespace std;class Int
{int value;
public:Int(int x = 0) :value(x){cout << "Create Int:" << endl;}~Int(){cout << "Destroy IntL:" << endl;}Int(const Int& it) :value(it.value){cout << "Copy Create Int:" << endl;}//对象加法Int operator+(const Int& it) const//修饰this指针指向{return Int(this->value + it.value);}//内置类型变量加法Int operator+(const int x) const//以引用返回对内存访问2次,传值对内存访问1次{return Int(this->value + x);}
};
//变量和对象相加
Int operator+(const int x, const Int& it)
{return it + x;
}
int main()
{Int a{ 10 }, b{ 20 }, c{ 0 };int x = 100;c = a + b;c = a + x;c = x + a;
}
3.1.5 局部对象内存分配图
#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
#include<iostream>
#include<assert.h>
#define SEQ_INIT_SIZE 10
class SeqList
{int data[SEQ_INIT_SIZE];//40int maxsize;//4int cursize;//4
public:SeqList() :maxsize(SEQ_INIT_SIZE), cursize(0){}~SeqList() {}
};
int main()
{SeqList seqa;return 0;
}
内存分配图
3.1.6 为什么系统要提供缺省构造函数和缺省赋值语句?
因为对象在整个函数生存期之内要被构建,所以要调用缺省构造函数。每个对象生存期结束时候要释放资源,所以要调用析构函数。
SeqList fun(SeqList seq)
{SeqList seqx;return seqx;
}
int main()
{SeqList seqa;SeqList seqb(seqa);SeqList seqc;seqc = seqb;return 0;
}
(1)在对象生存期过程中,要用一个对象初始化另一个对象,要调用缺省构造函数,函数在对象生存期内会使用。
(2)列表初始化只能使用在构造函数和拷贝构造函数里面,其他函数不能使用
(3)不允许使用一个对象初始化另一个对象应该如何操作?
将构造函数声明和拷贝构造函数声明加入私有部分,程序不在自动生成构造函数,所有对象都不能进行拷贝构造,只能构建对象和析构对象,不能用一个对象初始化另一个对象,两对象也不能进行赋值。
(4)是否可以将析构函数设置为私有?
不可以,对象虽然可以创建,但是不能释放。所有类的构造函数,拷贝构造函数,赋值语句重载函数,析构函数都不能随便设置私有。
3.1.7 对象内存分配图
#define SEQ_INIT_SIZE 10
#define SEQ_INC_SIZE 2
class SeqList
{int* data;int maxsize;int cursize;
public:SeqList() :maxsize(SEQ_INIT_SIZE), cursize(0){data = (int*)malloc(sizeof(int) * maxsize);}~SeqList() {free(data);data = NULL;}
};class Vector
{int* _first;int* _last;int* _end;
public:Vector() :_first(NULL), _last(NULL), _end(NULL){_first = (int*)malloc(sizeof(int) * SEQ_INC_SIZE);_last = _first;_end = _first + SEQ_INIT_SIZE;}~Vector(){free(_first);_first = _last = _end();}
};
int main()
{SeqList seqa;//SeqList seqb(seqa);不允许编译通过,因为seqa和sseqb指向同一块内存,导致释放两次,程序奔溃SeqList seqb;//seqb=seqa;不允许编译通过,会产生内存泄漏。当seqa和seqb指向同一块内存会导致同一块内存释放两次,然后seqb初始指向的堆内存没有释放,造成内存泄漏return 0;
}
vec._last-vec._first=maxsize;//元素个数大小
vec._end-vec._first=cursize;//容量大小
3.2 拷贝构造函数
3.2.1 何时需要自己定义拷贝构造函数和赋值语句?
在类的设计过程中,设计指针和指向内核态时,设计线程,要重新定义自己的拷贝构造函数和赋值语句。
3.2.2 深拷贝
//缺省拷贝构造函数SeqList(const SeqList& seq){//data = seq.data;//浅拷贝构造maxsize = seq.maxsize;cursize = seq.cursize;//深拷贝构造data = (int*)malloc(sizeof(int) * seq.maxsize);memcpy(data, seq.data, sizeof(int) * seq.cursize);}//缺省赋值函数SeqList& operator=(const SeqList& seq){if (this != &seq){//释放原有函数free(data);data = (int*)malloc(sizeof(int) * seq.maxsize);memcpy(data, seq.data, sizeof(int) * seq.cursize);maxsize = seq.maxsize;cursize = seq.cursize;}return *this;}
const只能修饰常方法,全局变量没有this指针
#define _CRT_SECURE_NO_WARNINGS
#include"stdio.h"
#include<iostream>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
using namespace std;class String
{char* str;String(char* p, int){str = p;}
public:String(const char* p = NULL) :str(NULL){if (p != NULL){str = new char[strlen(p) + 1];strcpy(str, p);}else{str = new char[1];*str = '\0';}}~String(){if (str != NULL){delete[] str;}str = NULL;}//类的成员函数,ostream& operator<<(ostream& out) const{if (str != NULL){out << str << endl;}return out;}String(const String& s) :str(NULL){str = new char[strlen(s.str + 1)];strcpy(str, s.str);}//缺省赋值语句String& operator=(const String& s){if (this != &s){delete[]str;str = new char[strlen(s.str) + 1];strcpy(str, s.str);}return *this;}//s3=s1+s2;String operator+(const String& s) const{char* p = new char[strlen(this->str) + strlen(s.str) + 1];strcpy(p, this->str);strcat(p, s.str);return String(p, 1);//调用私有拷贝构造函数}//s3=s1+"newdata";String operator+(const char* s) const{char* p = new char[strlen(this->str) + strlen(s) + 1];strcpy(p, this->str);strcat(p, s);return String(p, 1);//return *this+String(s);}
};
ostream& operator<<(ostream& out, const String& s)
{s << out;//s.operator<<(out);//operator<<(&s, out);return out;
}
//s3="newdata"+s1;
String operator+(const char* p, const String& s)
{return String(p) + s;
}int main()
{String s1("hxy");String s2("hello");String s3;//s3=s1+s2;//s3=s1+"newdata";//s3="newdata"+s1;
}
- 当函数的形参是类的对象
当函数的形参是类的对象,调用函数时,进行形参与实参结合时使用。
- 当函数的返回值是类对象
当函数的返回值是类对象,函数执行完成返回调用者时使用。理由也是要建立一个临时对象中,再返回调用者。
因为局部对象在离开建立它的函数时就消亡了,不可能在返回调用函数后继续生存,所以在处理这种情况时,编译系统会在调用函数的表达式中创建一个无名临时对象,该临时对象的生存周期只在函数调用处的表达式中。
所谓return对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象空间。如果返回的是变量,处理过程类似,只是不调用构造函数。
以引用返回不需要构建临时对象,局部变量或者局部变量地址不能以引用返回
所以不以引用返回,但是当函数中不存在赋值函数时候便可以认为可以用引用返回。
3.2.3 代码示例
构建将亡值对象
函数申请空间图
4. 函数重载
4.1C++编程特点
- 兼容于C语言
- class类编程
- template模板函数
- STL容器
4.2 C++调用函数顺序
class Object
{};Object obja;//.data
int main()
{Object objb; //.stackObject* p = new Object(); //.heapdelete p;//1.调用析构函数~Object 2.把堆空间还给系统return 0;
}
4.3 C++切片问题
继承关系和公有继承,子对象赋值给父对象才会产生切片问题,父对象不能赋值给子对象
class Object
{int value;
public:Object(int x=0) :value(x) {}
};
class Base :public Object
{int num;
public:Base(int x=0):Object(x+10),num(x) {}
};
5. 移动构造函数和移动赋值函数
5.1 引用类型
int& x = a;//左值引用
const int& y = a;//万能引用
int&& z = 10;//右值引用
5.2 移动构造函数
5.3 移动赋值函数
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<cassert>
using namespace std;class MyString
{char* str;
public:MyString(const char* p = nullptr):str(nullptr){if (p != nullptr){int n = strlen(p) + 1;str = new char[n];strcpy_s(str, n, p);}else{str = new char[1];*str = '\0';}}~MyString(){delete[] str;str = nullptr;}MyString(const MyString& s):str(nullptr){int n = strlen(s.str) + 1;str = new char[n];strcpy_s(str, n, s.str);}/*MyString& operator=(const MyString& s){if (this != &s){delete[]this->str;int n = strlen(s.str) + 1;this->str = new char[n];strcpy_s(this->str, n, s.str);}return *this;}*/void Print() const{cout << str << endl;}//移动构造函数:转移资源MyString(MyString&& s){this->str = s.str;s.str = nullptr;}//移动赋值函数MyString& operator=(MyString&& s){if (this != &s){delete[]this->str;this->str = s.str;s.str = nullptr;}return *this;}
};
MyString fun(const char* p)
{MyString tmp(p);return tmp;
}
int main()
{MyString s1("hello");s1 = fun("maria");return 0;
}
C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数相关推荐
- C#中的函数重载和构造函数
函数重载 在当你翻看别人的代码或查看C#中的自带的方法中,你或许可以看到有许多的名称相同的函数名/方法名,当在实际运许中却没有报错,这是为什么呢? 这就是C#中的函数重载,你可以发现在函数重载中的方法 ...
- C++文件头,命名空间,new和delete,内联函数,引用,函数重载,构造函数和析构函数,深拷贝和浅拷贝,explict,this指针
目 录 1 开始学习C++............................................................................... ...
- C#只能靠参数而不能靠返回值类型的不同来区分方法重载
重载必须要用参数来区分,传入不同参数,可以实现重载. 可以这样理解,你调用一个方法,如果有多个方法同名,系统必须要知道到底你要调用哪一个,参数可以帮助系统在方法入口处得到答案,他根据你给的参数就知道该 ...
- 设计模式-值类型与引用类型、深拷贝与浅拷贝、原型模式详解
一. 值类型和引用类型 1. 前言 (1). 分类 值类型包括:布尔类型.浮点类型(float.double.decimal.byte).字符类型(char).整型(int.long.short等). ...
- C++拷贝构造函数:深拷贝和浅拷贝
1 拷贝构造函数 它是一种特殊的构造函数,由编译器调用来完成一些基于同一类的其他对象的构件及初始化. 1.1 拷贝函数的调用场景: 1.值传递传递函数体 2.值传递从函数体返回(返回匿名对象) 3.用 ...
- .NET深入学习笔记(4):深拷贝与浅拷贝(Deep Copy and Shallow Copy)
今天继续利用准备WSE安全开发文章的空闲时间,完善<.NET深入学习笔记>系列(基本都是.Net重要的知识点,我都做了详细的总结,是什么.为什么.和怎么实现).想必很多人也接触过这两个概念 ...
- C++深拷贝与浅拷贝的区别-简单易懂
C++深拷贝与浅拷贝的区别-简单易懂 介绍 浅拷贝就比如像引用类型,而深拷贝就比如值类型. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同).对其中任何一个对象的改动都会影响另 ...
- javascript深拷贝和浅拷贝
在JavaScript中,存在着这样的两种拷贝方式.分别是:深拷贝和浅拷贝,这两种拷贝在实际中非常的常见,如果读者是一个阅读源码的爱好者,相信多多少少对深拷贝和浅拷贝有所了解. 一.浅拷贝 浅拷贝在现 ...
- 深拷贝与浅拷贝Object.assign()
深拷贝与浅拷贝 Object.assign()会身拷贝一个复杂类型 <!DOCTYPE html> <html lang="en"> <head> ...
- C# 克隆(Clone)中的深拷贝和浅拷贝
有时候在项目中需要得到一个对象在某个状态下的副本,为了避免重新创建一个对象再一一赋值,便可以使用克隆来处理,克隆分为浅拷贝和深拷贝. 浅拷贝对于值类型和String类型成员,浅拷贝会在副本中重新创建成 ...
最新文章
- 隐马尔科夫模型HMM(一)HMM模型
- 树莓派安装samba共享文件
- 【Android】附加Android源代码Androidandroid_gingerbread_javasrc
- python层次聚类分析_SPSS聚类分析:系统聚类分析
- springMVC--(讲解5)文件上传与传参测试
- 爬虫学习笔记(十五)——加密解密
- mysql 替代like_mysql用instr替代like查询
- EdgeGallery:聚焦 5 大行业场景,MEC 开源平台将 5G 能力拓展到边缘
- WN7 上IIS7运行asp+access网站出现错误: ADODB.Connection 错误 '800a0e7a'未找到提供程序。该程序可能未正确安装
- 程序人生:女程序员的求职奋斗史
- Java对MongoDb的CURD操作
- 公办低分二本_河南最适合“二本”考生的30所公办大学,录取分低,考生不要错过...
- oracle添加男女约束,Oracle如何给数据库添加约束过程解析
- Java餐厅点餐系统【附源码报告】
- Arduino学习笔记:基于LiquidCrystal库运行LCD1602
- 树莓派 --- 控制舵机转动代码Python
- jquery.seat-charts.1.1.15 选座座位插件的方法介绍
- vscode error: You have not concluded your merge
- SortPool (DGCNN) - An End-to-End Deep Learning Architecture for Graph Classification AAAI 2018
- C语言 IP地址的转换
热门文章
- 论文笔记——多源融合SLAM的现状与挑战
- 矩阵转置matlab的函数,【ZZ】Matlab矩阵操作
- vue 滑动置顶功能_CSS3 移动端 滚动置顶 吸顶
- yarn安装详细教程说明、升级教程、修改yarn的全局和缓存目录、yarn基本命令
- 眼科准分子激光治疗仪行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
- 正确的座机号码格式_简历里的手机号及座机号的标准写法是什么?正确书写才更可能求职成功!...
- 请教点击按钮时获得文本框中的字符进行操作问题
- matlab实现傅立叶变换6,实验六傅里叶变换及其反变换
- 在线制作证件照教程,只需30秒
- 米思齐(Mixly for Mac)官方版下载过程以及遇到的问题/解决方法