namespace mycode
{
void fun(int x)
{
cout<
static_cast
double a=3.1415926;
int b=static_cast(a);
访问全局/局部变量
int a=7;
inline void Case_Test()
{
double b=6.23451;
int a=static_cast(b);//
mycode::fun(a);
mycode::fun(::a);
}
decltype类型推导
- decltype推测某个表达式的数据类型
double f();
decltype(f()) sum=0;//推导出sum为double
int i=42;
double d=3.14;
decltype(i+d) e;//推导出i+d类型为double类型
第二章 C++语言进阶
指针
指针变量的数据类型必须要一致,不同类型的不能赋值
int a[10];
int *p;
p=a;//或p=&a[0]; 数组的首地址
指向数组的指针
int (*p)[3];//p是指针变量,指向含3个int元素的一维数组
a[i][j]=*(*(a+i)+j)
int a=5;
int *pa=&a;
int **ppa=&pa;
a=5; <-> *pa=5; <-> **ppa=5;
示例:
char **p;
char *name[]={"hello","good","world","bye",""};
p=name;
while (**p!=NULL)
{
cout<<*p++<
动态内存分配
栈区:局部数据
局部对象、函数形参、函数返回值存放在内存的栈区,随着函数的调用和参数的传递,在栈中分配内存,调用构造函数初始化对象;函数返回后,清除刚创建的局部数据,回收内存。所有操作由系统自动完成。
局部数据未初始化,则值为随机值。
堆区:局部变量
C++支持new和delete运算符,用于在堆中动态创建对象和释放对象。堆中的内存由程序员自由请求和分配,但必须由程序员负责使用delete释放。
使用new请求内存时,可能由于内存不足会造成失败,应该随时进行检测。
动态内存分配
int *p;
p=new int(5)//分配空间并初始化为5 *p=5
...
delete p;
多维的:
int *p;
p=new int[10];//分配10个int元素空间
*p=5;
*(p+1)=6//或者p[1]=6;
...
delete []p;
常指针和指向常量的指针
指向常量指针
char str1[]{"abcd"};
const char *pc=str1;
或char const *pc{str1};
pc所指向的字符串不能被修改。
pc[3]=‘a’; //错误
但pc可以指向别的字符串
char str2[]=“hello”;
pc=str2; //正确
常指针
char str1[]{"abcd"};
char * const pc{str1};
pc不能指向别的字符串,但可改变指针所指向的内容。
pc[2]=‘a’; //正确
char str2[]{“hello”};
pc=str2; //错误
指向常量的常指针
char str1[]{"abcd"};
const char * const pc=str1;
pc不能指向别的字符串,也不能改变指针所指向的内容。
pc[2]=‘a’; //错误
char str2[]=“hello”;
pc=str2; //错误
constexpr
常量表达式
const int max=20;//常量表达式
const int limit=max+1;//常量表达式
const int sz=getSize();//非常量表达式
constexpr变量:必须用常量表达式初始化
constexpr int mf=20;
constexpr int limit=mf+1;
constexpr int sz=getSize();//?
- 编译阶段确定初始值的常量可以声明为constexpr。
- 由编译器在编译阶段进行检查。
- 只有当getSize()是constexpr函数时,才能通过编译
constexpr方法
constexpr int square(int x)//如果没有"constexpr"则会报错
{
return x*x;
}
double a[square(9)]; //-> double a[81];
空指针(nullptr)
旧版本中使用NULL或者0表示空指针,但存在一些隐含的问题
int *p=nullptr;
void f(int);
void f(char *);
f(0)//错误
f(nullptr);//强类型检查
指向函数的指针
int add(int a,int b){return a+b;}
int sub(int a,int b){return a-b;}
int compute(int a,int b,int (*pf)(int,int)){
return pf(a,b);
}
compute(3,5,add);
conpute(3,5,sub);
引用的概念
引用是变量或对象的别名,建立引用时必须确定引用的对象,对引用的操作实际上就是对被引用者的操作。
int i=1;
int &ri=i;
//以后对ri的操作,实际上操作的是i
//可以认为ri和i在内存中占用相同的单元
引用只是别名,并不为其分配存储单元
void sum(int &a,int b){
a+=b;
}
int main(){
int x(5),y(4);
sum(x,y);
cout<
a
a
a和
x
x
x实质是同一个东西
函数返回指针
返回指向内存某处的指针
char* elem(char *s,int n){
return &s[n];
}
int main(){
char str[]="C++ Program";
char *pc=elem(str,5);
*pc='A';//将第五位的'P'改为'A'
cout<
返回对某个变量的引用
char &elem(char *s,int n){
return s[n];
}
int main(){
char str[]="C++ Program";
elem(str,5)='A';
cout<
不能返回指向局部变量的指针
char *elem(char *s,int n){
char c=s[n];
return &c;//c只是局部变量
}
char &elem(char *s,int n){
char c=s[n];//c只是局部变量
}
函数参数的默认值
int sum(int a,int b=2,int c=3){
return a+b+c;
}
string
string s(5,'a');//使用字符'a'重复5次构造s
vector
简介
vector a(4);//4个元素,默认初始为0
vector b(4);//1个元素,值为4
vector c(10,-1);//10个元素,初始为-1
vector d{2,3,5};//3个指定初始元素
迭代器遍历元素
vector::iterator it;//迭代器循环不能用remove和push_back
数组的迭代器遍历
int a[]{2,3,6,1,9,0};
int *beg=begin(a);
int *end=end(a);
for (int *p=beg;p!=end;++p)
cout<<*p<
第三章 类和对象
在堆中创建对象
int main(){
Fraction *pFraction1=new Fraction;
pFraction1->setNum(8);
(*pFraction1).setDen(14);
pFraction1->output();
delete pFraction1;
}
注意这里的*和->的用法
使用new记得delete
this作用
void Fraction::setNum(int num){
this->num=num;
}
default
在构造函数中如果没有自己定义的那么系统会提供一个没有实质功能的默认构造函数也就是Student() {} 。如果我们写出了构造函数,那么这个默认的就还需自己写
class Student{
public:
Student(int _age):age(_age){}//写出这个之后无默认构造函数,需要写下一行
Student()=default;//生成默认构造函数
Student(){}//与上一行同样效果
//下面是default的拷贝构造
Student(Student &x){age=x.age;h=x.h;}
Student(Student &x)=default;//与上一行是一样的
private:
int age;
}
但在拷贝构造里面也可以用default(见上代码)
类内初始化
C++11中也可以在类内定义初始值
class Student
{
...
private:
int age=1,h=2;
}
委托构造函数
class Student
{
public:
Student(int _age,int _h):age(_age),h(_h){cout<<"GZ"<
委托构造时,先调用委托的版本构造函数,再调用构造函数自身。
类型转换构造函数
//class Student同上
int main()
{
Student Karry;
Karry=5;//会调用class中Student(int x) 构造函数构造出一个临时的
}
拒绝隐式转换——explicit
explicit Student(int x):Student(x,7){cout<<"WT"<
这样程序就会报错,Karry=5无法执行
explicit要求显示调用这种类型转换
Karry=Student(5);或者是Karry=static_cast(5);
拷贝构造函数
用途:建立新对象时,用一个已经存在的同类型对象去初始化这个新对象
每个类必须拥有一个,可以自定义,若未定义的话系统会自动生成缺省的拷贝构造,用于复制数据成员完全相同的新对象
class Student{
public:
Student(const Student& t)
{
age=t.age;h=t.h;
cout<<"ZDY"<
一下四种都会调用拷贝构造函数
Student Karry=Carry;
Student Karry=Student(Carry);
Student Karry(Carry);
Student Karry{Carry};
对象作为函数返回值
Student Copy(Student f){//f也就是第三次,创建了f
Student f1; //第4次:定义对象
f1=f;
return f1; //第5次:返回临时对象temp
}
int main(){
Student f2(4); //第1次:定义对象
Student f3; //第2次:定义对象
f3=Copy(f2); //第3次:参数传递
//f3=temp;
f3.print();
}
对象的初始化
Student f2=...这个叫初始化;Student f2;f2=这个叫赋值(需要重载=)
阻止拷贝构造
Student(const Student& t)=delete;
若无,程序会自动产生拷贝构造
default:强制生成缺省版本函数(构造,析构,拷贝,赋值运算符)
delete:禁止生产缺省版本函数
Array1D类分析
class Array1D{
...
private:
int *pData;
int size;
}
构造函数-动态分配内存
Array1D::Array1D(int *p,int s){
pData=new int[s];
size=s;
for(int i=0;i
析构函数
Array1D::~Array1D(){
delete[] pData;
}
右值引用
C++中的引用必须绑定到1个左值,无法定义1个常量、表达式的引用
void fun(int& a);
int b=2,c=4;
fun(b);//正确
fun(b+c);//错误,将b+c传入&a中??
函数参数修改为const int&后可以传递,无法修改
C++11增加&&可以引用临时的右值
void fun(int &&a);
fun(b+c);
增加右值引用喝Move语义
class Array1D{
public:
Array1D(const Array1D& a); //拷贝构造
Array1D(Array1D&& a); //移动构造
Array1D& operator=(const Array1D& a); //拷贝赋值
Array1D& operator=(Array1D&& a); //移动赋值
...
};
如果要增加右值引用和move语义,在原来正常拷贝构造与拷贝赋值的基础上,增加移动构造和移动赋值方法。
移动构造函数实现
Array1D::Array1D(Array1D &&a){
size=a.size;
pData=a.pData;
a.size=0;
a.pData=nullptr;//delete?
//移动时直接接管参数对象的数据,然后将其置空
}
普通拷贝Array1D b(a)需要内存分配
Array1D ff(){
Array1D temp(100);
return temp;
}
Array1D c=ff();//内部数据移动,初始化构造c对象,不用为c分配内存
强制移动的示例
Array1D a(100); Array1D b(move(a));
通过move操作,强制a移动并移动构造对象b 移动后,a无效了,以及被移走
静态数据成员
class Student{
public:
static int cnt;//声明
Student(){cnt++;}
~Student(){cnt--;}
...
};
int Student::cnt=0;//定义并初始化
友元
class Point{
private:
double x,y;
public:
Point(double xx=0, double yy=0):x(xx),y(yy){}
double getX() { return x; }
double getY() { return y; }
friend double getDis(const Point& a,const Point& b);
//若不用友元则不能直接用
};
double getDis( const Point &a, const Point &b){
double dx,dy;
dx=b.getX()-a.getX();//不用友元则不能直接访问b.x等
dy=b.getY()-a.getY();
//用友元则可以 dx=b.x-a.x;dy=b.y-a.y;
return sqrt(dx*dx+dy*dy);
}
getDis只是类外部定义的普通全局函数,被声明为Point类的友元函数。
不能在函数名前加Point::前缀
成员函数版本
double Point::getDis( const Point &a){
double dx,dy;
dx=x-a.x;
dy=y-a.y;
return sqrt(dx*dx+dy*dy);
}
...
cout<<"distance:"<
常成员函数(*)
class Array1D{
public:
...
int getSize()const;
int getValue(int index) const;
void setValue(int index, int value);
private:
...
};
const Array1D a1(…);
Array1D a2(…);
a1.getValue(index);//正确
a1.setValue(index, value);//错误
const Array1D *pArray1=&a2;
pArray1->setValue(index, value);//错误
Array1D * const pArray2=&a2;
pArray2->setValue(index, value);//正确
指向常量的指针,可以修改其所指向对象的内容
第四章 运算符重载
成员函数重载+
class Fraction{
private:
int num,den;
public:
...
Fraction operator+(const Fraction& f) const{
return Fraction(num*f.den+den*f.num,den*f.den);
}
}
...
Fraction a(1,4),b(1,3),c;
c=a+b;//c=a.operator+(b);
operator+ 相当于函数名
友元函数重载-
class Fraction{
...
friend Fraction operator-(const Fraction& a,const Fraction& b);
}
Fraction operator-(const Fraction& a,const Fraction& b){
return Fraction(a.num*b.den-a.den*b.num,a.den*b.den);
}
...
c=a-b;
Fraction a,b a-b返回的是一个Fraction
重载赋值运算符
class Fraction{
...
void operator=(const Fraction& f);
}
void Fraction::operator=(const Fraction& f){
num=f.num;
den=f.den;
}
...
c=a;//如果是c=b=a呢?
如果是c=b=a则会出现语法错误,=运算符右结合先执行b=a,返回值为void无法赋值给a
问题的解决1
class Fraction{
...
Fraction operator=(const Fraction& f);
};
Fraction Fraction::operator=(const Fraction& f){//返回一个Fraction
num=f.num;den=f.den;
return Fraction(num,den);//效率太低
}
效率太低,我们选择传引用
问题的解决2
class Fraction{
...
Fraction& operator=(const Fraction& f);
};
Fraction& Fraction::operator=(const Fraction& f){//返回引用
num=f.num;den=f.den;
return *this;//效率高
}
c=b=a;先执行b=a,b被赋值后返回b的引用,再赋值给c,最后的返回值丢弃
重载+=运算符
class Fraction{
...
Fraction& operator+=(const Fraction& f);
}
Fraction& Fraction::operator+=(const Fraction& f){
num=num*f.den+f.num*den;den=den*f.den;
normalize();
return *this;
}
Fraction f1(3,4),f2(2,3);
f1+=f2;
-(负号)运算符
class Fraction{
...
Fraction operator-()const;
}
Fraction Fraction::operator-()const{
return Fraction(-num,den);
}
...
c=-b;
错误示范
Fraction Fraction::operator-(const Fraction& f){
num=-num;
return *this;
}
这个时候改变了传入参量的值,实际上不应该让它改变
友元函数版本
class Fraction{
...
friend Fraction operator-(const Fraction& );
}
Fraction operator-(const Fraction& f){
return Fraction(-f.num,f.den);
}
c=-b;
分数与实数类型转换
class Fraction{
...
Fraction(int n):Fraction(n,1){}
}
Fraction a(3,4),b,c;
b=a+2;//先将2转化为Fraction,然后与a相加赋值给b
//c=2+a; 错误,a不能转换为整数
//friend Fraction operator+(const Fraction& ,const Fraction& );
前置、后置++
++和--运算符也可以重载,但为了区分前置和后置运算。C++约定把前置运算符重载为单目运算符函数,即表达式++a;解释为a.operator++()
把后置运算符看成双目运算符,在参数表内放置一个整型参数,该参数没有任何作用,只是用来作为后置运算符的标识。
a++;解释为a.operator++(int)
class Fraction{
...
Fraction& operator++();
Fraction operator++(int);
}
//前置 ++a
Fraction& Fraction::operator++(){
num+=den;
return *this;//传递引用 *this 就是当前的 改变
}
//后置 a++
Fraction Fraction::operator++(int a){
Fraction f(*this);//++之前的给f
num+=den;
return f;//返回的是f(++之前未改变)
}
int main(){
Fraction f(3,4);
(f++).output();
(++f).output();
return 0;
}
友元函数实现
class Fraction{
friend Fraction& operator++(Fraction& f);
friend Fraction operator++(Fraction& f,int)
}
Fraction& operator++(Fraction& f){
f.num+=f.den;//先修改f
return f;//再返回f引用
}
Fraction operator++(Fraction& f,int a){
Fraction temp(f);
f.num+=f.den;
return temp;
}
int main(){
Fraction f(3,4);
(f++).output();
(++f).output();
return 0;
}
我不理解为什么重载++的友元函数时候需要传入参量Fraction& f
类型转换运算符
class Fraction{
...
operator double() const{
return getValue;
}
}
int main(){
Fraction f(3,4);
cout<<3.5+f<
若声明explicit operator double() const{return getValue();}
则需要cout<<3.5+double(f)<(f)<
完善Array1D
重载赋值运算符
Array1D& Array1D::operator=(const Array1D& a){
pData=a.pData;
size=a.size;
}
如果类中包含指针成员,缺省赋值运算符直接在,指针之间赋值,导致内存问题。
Array1D& Array1D::operator=(const Array1D& a)
{
if(this==&a) return *this;//判断是否自我赋值
delete[] pData;//先释放当前内存
copyData(a.pData, a.getSize());
return *this;
}
增加右值引用和Move语义
class Array1D{
public:
Array1D(const Array1D& a);//拷贝构造
Array1D(Array1D&& a);//移动构造
Array1D& operator=(const Array1D& a);//拷贝构造
Array1D& operator=(Array1D&& a);//移动构造
}
移动赋值运算符
Array1D& Array1D::operator=(Array1D &&a)
{
if(this==&a) return *this;
delete []pData;
size=a.size;
pData=a.pData;
a.size=0;
a.pData=null;//a清空
}
重载下标运算符
class Array1D{
...
int& operator[](int i){return pData[i];}
const int& operator[](int i)const{return pData[i];}
}
...
array2[2]=10;
Array2D
动态内存分配
//申请内存
int **p;
p=new int*[row];
for (int i=0;i
class Array2D{
public:
Array2D(int row);
~Array2D();
Array1D& operator[](int index);//返回的是1维
private:
Array1D *pData;//很多1维
int row;
}
Array2D::Array2D(int r):row(r){
pData=new Array1D[row];//调用缺省构造函数
}
Array2D::~Array2D(){
delete[] pData;
}
Array1D& Array2D::operator[](int index){
return pData[index];
}
int main()
{
Array2D a(3);
a[0]=Array1D(...);
a[2][1]=5;//可以这样 两次[]
}



