文章目录

  • 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:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数相关推荐

  1. C#中的函数重载和构造函数

    函数重载 在当你翻看别人的代码或查看C#中的自带的方法中,你或许可以看到有许多的名称相同的函数名/方法名,当在实际运许中却没有报错,这是为什么呢? 这就是C#中的函数重载,你可以发现在函数重载中的方法 ...

  2. C++文件头,命名空间,new和delete,内联函数,引用,函数重载,构造函数和析构函数,深拷贝和浅拷贝,explict,this指针

     目 录 1      开始学习C++............................................................................... ...

  3. C#只能靠参数而不能靠返回值类型的不同来区分方法重载

    重载必须要用参数来区分,传入不同参数,可以实现重载. 可以这样理解,你调用一个方法,如果有多个方法同名,系统必须要知道到底你要调用哪一个,参数可以帮助系统在方法入口处得到答案,他根据你给的参数就知道该 ...

  4. 设计模式-值类型与引用类型、深拷贝与浅拷贝、原型模式详解

    一. 值类型和引用类型 1. 前言 (1). 分类 值类型包括:布尔类型.浮点类型(float.double.decimal.byte).字符类型(char).整型(int.long.short等). ...

  5. C++拷贝构造函数:深拷贝和浅拷贝

    1 拷贝构造函数 它是一种特殊的构造函数,由编译器调用来完成一些基于同一类的其他对象的构件及初始化. 1.1 拷贝函数的调用场景: 1.值传递传递函数体 2.值传递从函数体返回(返回匿名对象) 3.用 ...

  6. .NET深入学习笔记(4):深拷贝与浅拷贝(Deep Copy and Shallow Copy)

    今天继续利用准备WSE安全开发文章的空闲时间,完善<.NET深入学习笔记>系列(基本都是.Net重要的知识点,我都做了详细的总结,是什么.为什么.和怎么实现).想必很多人也接触过这两个概念 ...

  7. C++深拷贝与浅拷贝的区别-简单易懂

    C++深拷贝与浅拷贝的区别-简单易懂 介绍 浅拷贝就比如像引用类型,而深拷贝就比如值类型. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同).对其中任何一个对象的改动都会影响另 ...

  8. javascript深拷贝和浅拷贝

    在JavaScript中,存在着这样的两种拷贝方式.分别是:深拷贝和浅拷贝,这两种拷贝在实际中非常的常见,如果读者是一个阅读源码的爱好者,相信多多少少对深拷贝和浅拷贝有所了解. 一.浅拷贝 浅拷贝在现 ...

  9. 深拷贝与浅拷贝Object.assign()

    深拷贝与浅拷贝 Object.assign()会身拷贝一个复杂类型 <!DOCTYPE html> <html lang="en"> <head> ...

  10. C# 克隆(Clone)中的深拷贝和浅拷贝

    有时候在项目中需要得到一个对象在某个状态下的副本,为了避免重新创建一个对象再一一赋值,便可以使用克隆来处理,克隆分为浅拷贝和深拷贝. 浅拷贝对于值类型和String类型成员,浅拷贝会在副本中重新创建成 ...

最新文章

  1. 隐马尔科夫模型HMM(一)HMM模型
  2. 树莓派安装samba共享文件
  3. 【Android】附加Android源代码Androidandroid_gingerbread_javasrc
  4. python层次聚类分析_SPSS聚类分析:系统聚类分析
  5. springMVC--(讲解5)文件上传与传参测试
  6. 爬虫学习笔记(十五)——加密解密
  7. mysql 替代like_mysql用instr替代like查询
  8. EdgeGallery:聚焦 5 大行业场景,MEC 开源平台将 5G 能力拓展到边缘
  9. WN7 上IIS7运行asp+access网站出现错误: ADODB.Connection 错误 '800a0e7a'未找到提供程序。该程序可能未正确安装
  10. 程序人生:女程序员的求职奋斗史
  11. Java对MongoDb的CURD操作
  12. 公办低分二本_河南最适合“二本”考生的30所公办大学,录取分低,考生不要错过...
  13. oracle添加男女约束,Oracle如何给数据库添加约束过程解析
  14. Java餐厅点餐系统【附源码报告】
  15. Arduino学习笔记:基于LiquidCrystal库运行LCD1602
  16. 树莓派 --- 控制舵机转动代码Python
  17. jquery.seat-charts.1.1.15 选座座位插件的方法介绍
  18. vscode error: You have not concluded your merge
  19. SortPool (DGCNN) - An End-to-End Deep Learning Architecture for Graph Classification AAAI 2018
  20. C语言 IP地址的转换

热门文章

  1. 论文笔记——多源融合SLAM的现状与挑战
  2. 矩阵转置matlab的函数,【ZZ】Matlab矩阵操作
  3. vue 滑动置顶功能_CSS3 移动端 滚动置顶 吸顶
  4. yarn安装详细教程说明、升级教程、修改yarn的全局和缓存目录、yarn基本命令
  5. 眼科准分子激光治疗仪行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  6. 正确的座机号码格式_简历里的手机号及座机号的标准写法是什么?正确书写才更可能求职成功!...
  7. 请教点击按钮时获得文本框中的字符进行操作问题
  8. matlab实现傅立叶变换6,实验六傅里叶变换及其反变换
  9. 在线制作证件照教程,只需30秒
  10. 米思齐(Mixly for Mac)官方版下载过程以及遇到的问题/解决方法