Fork me on GitHub

C++-封装篇


string类型

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include<string>
using namespace std;

int main()
{
string name = "ZhangSan";
string hobby("football");
cout<<name<<hobby<<endl;

return 0;
}
初始化string对象的方式
string s1; s1为空串
string s2(“ABC”) 用字符串字面值初始化s2
string s3(s2) 将s3初始化为s2的一个副本
string s4(n,’c’) 将s4初始化为字符’c’的n个副本

string的常用操作

1
2
3
4
5
6
7
s.empty()
s.size()
s[n]
s1 + s2
s1 = s2
v1 == v2
v1 != v2

注意:s1 = "Hello" + "World"; // 错误,必须要有string变量

类内定义与内联函数

1
2
3
4
5
6
7
8
9
10
class Student
{
public:
void setAge(int _age){age = _age}
int getAge(){return age;}
void study(){// to do}
private:
string name;
int age;
};

如果在类内定义,在相当于把它们定义为了内联函数

类外定义

同文件类外定义

1
2
3
4
5
6
7
8
9
10
11
class Car
{
public:
void run();
void stop();
void changeSpeed();
};

void Car::run(){}
void Car::stop(){}
void Car::changeSpeed(){}

分文件类外定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Car.h

class Car
{
public:
void run();
void stop();
void changeSpeed();
};

// Car.cpp

#include "Car.h"
void Car::run()
{}
void Car::stop()
{}
void Car::changeSpeed()
{}

构造函数

栈区:int x = 0;int *p = NULL; // 对象的申请与释放都由计算机来完成,不需要我们自己释放
堆区:int *p = new int[20];    // 需要自己申请内存和释放内存
全局区:存储全局变量和静态变量
常量区:string str = "hello";
代码区:存储逻辑代码的二进制

构造函数在对象实例化时被自动调用,且只调用一次

  • 构造函数与类同名
  • 构造函数没有返回值
  • 构造函数可以有多个重载形式
  • 实例化对象时仅且用到一个构造函数
  • 用户没有定义构造函数时,编译器自动生成一个构造函数
1
2
3
4
5
6
7
8
9
class Student
{
public:
Student(){m_strName = "jim";}
Student(string name)
{m_strName = name;}
private:
string m_strName;
};

构造函数的初始化列表

1
2
3
4
5
6
7
8
class Student
{
public:
Student():m_strName("Jim"),m_iAge(10){}
private:
string m_strName;
int m_iAge;
};

初始化列表先于构造函数执行
初始化列表只能用于构造函数
初始化列表可以同时初始化多个数据成员

初始化列表的必要性

1
2
3
4
5
6
7
class Circle
{
public:
Circle(){m_dPi = 3.14;} // 错误
private:
const double m_dPi;
};
1
2
3
4
5
6
7
class Circle
{
public:
Circle():m_dPi(3.14){}
private:
const double m_dPi;
};

必须用初始化列表

拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student
{
public:
Student()
{cout<<"Student";}
private:
string m_strName;
};

int main(void)
{
Student stu1;
Student stu2 = stu1;
Student stu3(stu1);

return 0;
}

这里写图片描述

只打印了一个student,是因为他只调用了一次构造函数,stu2和stu3调用的是默认的拷贝构造函数

1
2
3
4
5
6
7
8
class Student
{
public:
Student(){m_strName = "jim";}
Student(const Student &stu){} // 拷贝构造函数
private:
string m_strName;
};

析构函数

1
2
3
4
5
6
7
8
class Student
{
public:
Student(){cout<<"Studnet"<<endl;}
~Student(){cout<<"~Student"<<endl;}
private:
string m_strName;
};

析构函数不能加任何参数,并且不能重载

析构函数的必要性

1
2
3
4
5
6
7
8
class Student
{
public:
Student(){m_pName = new char[20];}
~Student(){delete []m_pName;}
private:
char *m_pName;
};
  • 如果没有自定义的析构函数则系统自动生成
  • 析构函数在对象销毁时自动调用
  • 析构函数没有返回值,没有参数也不能重载

对象的生命过程

申请内存->初始化列表->构造函数->参与运算->析构函数->释放内存

对象成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Coordinate
{
public:
Coordinate(int x,int y);
private:
int m_iX;
int m_iY;
};
Coordinate::Coordinate(int x,int y)
{
m_iX = x;
m_iY = y;
}
class Line
{
public:
Line(int x1,int y1,int x2,int y2);
private:
Coordinate m_coorA;
Coordinate m_coorB;
};

Line::Line(int x1,int y1,int x2,int y2):m_coorA(x1,y1),m_coorB(x2,y2)
{
cout<<"Line"<<endl;
}
int main(void)
{
Line *p = new Line();

delete p;
p = NULL;
return 0;
}

先实例化成员对象m_coorAm_coorB,然后实例化Line对象

浅拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Array
{
public:
Array(){m_iCount = 5;}
Array(const Array &arr)
{
m_iCount = arr.m_iCount;
}
private:
int m_iCount;
};

int main()
{
Array arr1;
Array arr2 = arr1;
return 0;
}

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Array
{
public:
Array(){m_iCount = 5;m_pArr = new int[m_iCount];}
Array(const Array &arr)
{
m_iCount = arr.m_iCount;
m_pArr = new int[m_iCount];
for(int i = 0 ; i < m_iCount ; i++)
m_pArr[i] = arr.m_iCount[i];
}
private:
int m_iCount;
int *m_pArr;
};

int main()
{
Array arr1;
Array arr2 = arr1;
return 0;
}

当成员中有指针时,需要使用深拷贝,避免两个指针指向同一块内存单元,析构时,会析构两次,造成系统崩溃。

this指针

指向对象本身的指针

常对象成员和常成员函数

1
2
3
4
5
6
7
8
class Coordinate
{
public:
Coordinate(int x,int y);
private:
const int m_iX;
const int m_iY;
};

常对象成员必须通过初始化列表进行初始化

1
2
Coordinate::Coordinate(int x,int y):m_iX(x),m_iY(y)
{}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Line
{
public:
Line(int x1,int y1,int x2,int y2);
private:
const Coordinate m_coorA;
const Coordinate m_coorB;
};

Line::Line(int x1,int y1,int x2,int y2):
m_coorA(x1,y1),m_coorB(x2,y2)
{
cout<<"Line"<<endl;
}

int main(void)
{
Line *p = new Line(2,1,6,4);

delete p;
p = NULL;
return 0;
}

常成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Coordinate
{
public:
Coordinate(int x,int y);
void changeX() const;
void changex();
private:
int m_iX;
int m_iY;
};

void Coordinate::changeX() const
{
m_iX = 10; // 错误
}

void Coordinate::changeX()
{
m_iX = 20; // 正确
}
  • 声明的时候也要加const
  • 在常成员函数中不能修改数据成员的值
  • 主要是因为参数中隐含的this指针被const修饰了

常指针与常引用

常引用只能调用常成员函数

1
2
3
4
5
6
7
8
9
10
int main(void)
{
Coordinate coor1(3,5);
const Coordinate &coor2 = coor1;
const Coordinate *pCoor = &coor1;
coor1.printInfo();
coor2.getX(); // 错误,因为getX()不是常函数
pCoor->getY(); // 错误,因为getY()不是常函数
return 0;
}
1
2
3
4
5
6
7
8
9
10
int main()
{
Coordinate coor1(3,5);
Coordinate coor2(7,9);
Coordinate * const pCoor = &coor1;
pCoor->getY();
pCoor = coor2; // 错误,道理和前面的修饰*p还是修饰P是一样的
pCoor->printInfo();
return 0;
}
坚持原创技术分享,您的支持将鼓励我继续创作
-------------本文结束感谢您的阅读-------------
0%