char* p=nullptr;//空指针,没有内存地址空间
p=new char[100];
*p='\0';//空字符串——指向地址空间,地址空间存放\0,有内存地址
class FileMan
{
FILE* fp;//文件类型指针
public:
FileMan(const char* filename)
:fp(nullptr)
{
fopen_s(&fp, filename, "w");//文件资源不占当前内存,为内核资源
}
~FileMan()
{
fclose(fp);
fp = nullptr;
}
};
#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;
}
};
防止自赋值
(lvalue左值,xvalue僵王值,rvalue右值,prvalue纯右值)
1.左值和将亡值合称泛左值,纯右值和将亡值合称右值。
2.函数执行时生成将亡值,函数结束将亡值死亡。
3.严格来讲,“左值"是表达式的结果的一种属性,但更为普遍地,我们通常用"左值"来指代左值表达式。所谓左值表达式,就是指求值结果的值类别为左值的表达式。通常我们无需区分"左值"指的是前者还是后者,因为它们表达的是同一个意思,不会引起歧义。在后文中,我们依然用左值指代左值表达式。对于纯右值和将亡值,亦然。
4.右值引用
右值引用只能引用纯右值
int&& c=10;
//右值拷贝构造(优先调用构建将亡值对象)
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;
}
① 创建对象
② 初始化对象中的属性
③类型转换(可以将内置类型转换为自己设计的类型,从而给变量进行赋值,当构造函数只有一个参数时,才能进行类型转换)
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;
类型强转函数
//重载整型强转运算符,对象强转赋值个变量
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;
}
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);
}
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;
}
#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;//容量大小
在类的设计过程中,设计指针和指向内核态时,设计线程,要重新定义自己的拷贝构造函数和赋值语句。
//缺省拷贝构造函数
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对象,实际上是调用拷贝构造函数把该对象的值拷入临时对象空间。如果返回的是变量,处理过程类似,只是不调用构造函数。
以引用返回不需要构建临时对象,局部变量或者局部变量地址不能以引用返回
所以不以引用返回,但是当函数中不存在赋值函数时候便可以认为可以用引用返回。
class Object
{};
Object obja;//.data
int main()
{
Object objb; //.stack
Object* p = new Object(); //.heap
delete p;//1.调用析构函数~Object 2.把堆空间还给系统
return 0;
}
继承关系和公有继承,子对象赋值给父对象才会产生切片问题,父对象不能赋值给子对象
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) {}
};
int& x = a;//左值引用
const int& y = a;//万能引用
int&& z = 10;//右值引用
#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;
}