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
#include
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;

C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第1张图片

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 1
	String& 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;
	}
#endif
	char* 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
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
#include
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
#include
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
#include
#define SEQ_INIT_SIZE 10
class SeqList
{
    int data[SEQ_INIT_SIZE];//40
    int maxsize;//4
    int cursize;//4
public:
    SeqList() :maxsize(SEQ_INIT_SIZE), cursize(0)
    {
    }
    ~SeqList() {}
};
int main()
{
    SeqList seqa;
    return 0;
}

内存分配图
C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第2张图片

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;
}

C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第3张图片
C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第4张图片

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
#include
#include
#include
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对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象空间。如果返回的是变量,处理过程类似,只是不调用构造函数。

以引用返回不需要构建临时对象,局部变量或者局部变量地址不能以引用返回
所以不以引用返回,但是当函数中不存在赋值函数时候便可以认为可以用引用返回。

C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第5张图片

3.2.3 代码示例

构建将亡值对象
C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第6张图片
函数申请空间图
C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第7张图片

4. 函数重载

4.1C++编程特点

  • 兼容于C语言
  • class类编程
  • template模板函数
  • STL容器

4.2 C++调用函数顺序

class Object
{};

Object obja;//.data
int main()
{
    Object objb; //.stack

    Object* p = new Object(); //.heap

    delete 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) {}
};

C++ 7:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第8张图片

5. 移动构造函数和移动赋值函数

5.1 引用类型

int& x = a;//左值引用
const int& y = a;//万能引用
int&& z = 10;//右值引用

5.2 移动构造函数

5.3 移动赋值函数

#include
#include
#include
#include
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:深拷贝和浅拷贝,,值类型,构造函数和拷贝构造函数,函数重载,移动构造函数和移动赋值函数_第9张图片


你可能感兴趣的:(C++(线上),c++,开发语言)