- 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=' ';//空字符串——指向地址空间,地址空间存放 ,有内存地址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 深拷贝构造函数
#include1.5 为什么使用深拷贝构造函数?#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存放 } else { str = new char[1]; *str = ' ';//空字符串 } } ~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; } };
防止自赋值
2. 值类型2.1 值类型定义(lvalue左值,xvalue僵王值,rvalue右值,prvalue纯右值)
- 左值:可以取地址
- 右值:不能取地址,无名对象
- 将亡值:在表达式运行过程中,所产生的无名实体
- 纯右值:字面常量
- 将亡值:在函数运行过程中产生的临时对象
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 = ' ';
}
} // 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 构造函数用途
① 创建对象
② 初始化对象中的属性
③类型转换(可以将内置类型转换为自己设计的类型,从而给变量进行赋值,当构造函数只有一个参数时,才能进行类型转换)
- (1)将内置类型转换为自己设计的类型
如不允许隐式构造,则加上explicit关键字
explicit关键字详解
#includeusing 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) 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类
#include3.1.5 局部对象内存分配图#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; }
#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; }
内存分配图
因为对象在整个函数生存期之内要被构建,所以要调用缺省构造函数。每个对象生存期结束时候要释放资源,所以要调用析构函数。
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)是否可以将析构函数设置为私有?
不可以,对象虽然可以创建,但是不能释放。所有类的构造函数,拷贝构造函数,赋值语句重载函数,析构函数都不能随便设置私有。
#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#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 = ' '; } } ~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语言
- class类编程
- template模板函数
- STL容器
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) {}
};
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 = ' '; } } ~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 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; }



