课程:
C++数据类型:https://www.runoob.com/cplusplus/cpp-data-types.html
C++远征之离港篇:https://www.imooc.com/learn/381
C++远征之封装篇(上):https://www.imooc.com/learn/382
C++远征之封装篇(下):https://www.imooc.com/learn/405
目标
读懂源码,简单编写功能代码。
开发工具Clion:下载,安装,激活。File -> Settings -> Build,Execution,Deployment -> Toolchains -> MinGW,下载编译工具。
默认多文件项目模式:新建项目FIle -> new project 新建项目,加载cmake信息、IDE debug图标点亮。
添加单文件运行插件:
《Clion单个C++源文件编译执行的配置方法、Windows下CLion中文乱码最有效的解决方式》
注意:Editor -> File Encodeings -> utf8+utf8+utf8 ,main第一行添加system("chcp 65001 > nul")。
1、数据基础
a.数组、字符串、指针、结构体
1、数组(纯粹相同类型元素)
- 一维数组:double balance[5] = {1000.0, 2.0, 3.4};;
- 多维数组:
int a[3][4] = {
{0, 1, 2, 3} , /* 初始化索引号为 0 的行 */
{4, 5, 6, 7} , /* 初始化索引号为 1 的行 */
{8, 9, 10, 11} /* 初始化索引号为 2 的行 */
};
2、字符串
- 定长字符串
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; //或
char greeting[] = "Hello";
- 常用函数
strcpy( dst_str, src_str ) //覆盖到
strcat( str1, str2 ) //连接
strlen( s1 )
strcmp(s1, s2);
//如果 s1 和 s2 是相同的,则返回 0;如果 s1s2 则返回值大于 0
strchr(s1, ch)
//返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置:即返回之后的字符串是 s1[:xx]
strstr(s1, s2)
//同上,返回字符串余下所有(包含)
3、指针与引用(地址vs别名)
- 变量定义:
int * p; //指针:第一个数组元素、或区块的第一个字符的地址
int ** pptr;
int * p[10];
int & q = a; //引用:变量别名、修改则拷贝新到副本
- 传递函数值、返回值
double * getAverage(int *arr, int size);
double * getAverage(int *arr, int size){}
void & swap(int & x, int & y);
void & swap(int & x, int & y){}
4、时间标准库
- clock()
start_t = clock();
for(i=0; i< 10000000; i++) { }
end_t = clock();
total_t = (double)(end_t - start_t) / CLOCKS_PER_SEC;
5、结构体
- .点访问成员
- 定义
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
//定义结构类型、同时定义一个变量(空)
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
- 结构类型的指针参数
void printBook( struct Books * book );
void printBook( struct Books * book )
{
cout << "书标题 : " << book->title <author <subject <book_id <
b.const、&和指针地址
与define不同:define是宏定义,预处理阶段完成替换(如同字符串替换),不检查类型。
6、const、&和指针地址
- 对象访问:obj.val,指针访问:p->val、或者 (*p).val;
- const修饰指针
int x=3; int const * p=&x;
//p=&y 正确:const*p是 别名的地址只读[不变],别名的值可以改变
//*p=4 错误:别名的地址只读
变量名 存储地址 存储内容
x &x 3
p &p[const*只读] &x
int x=3; int * const p=&x;
//p=&y 错误:const p是 别名不变,内容不可变
变量名 存储地址 存储内容
x &x 3
p &p &x[const只读]
const int x=3; const int * const p=&x;
//p=&y; p=4错误:别名只读、别名的地址也只读
变量名 存储地址 存储内容
x &x 3[const只读]
p &p[const只读] &x[const只读]
- const修饰引用
int x=3; int const & y=x;
//x=10 正确
//y=20 错误:y的存储地址锁定,无法产生内容改变
变量名 存储地址 存储内容
x &x 3
y &x[const只读] -(暂无->>差异副本)
这样定义则把 y 锁定到 x ,且无法定义副本改变
- 注意内容锁定只读、还是指针或别名只读
int x=3; int const * y=&x;
//*y=5 错误:y的指针锁定
变量名 存储地址 存储内容
x &x 3
y &y[const只读] &x
这样同样把 y 锁定到 &x ,不变
int const x=3;
//int * y=&x 错误:x的值与地址在初始化后都只读,
使用 int const * y=&x 或 int const * const z=&x
c.类的定义和使用
这里std::cout
在引用use namespace std;
,注意命名空间。
//0.引用
#include
#include "TV.cpp"
//1. `从栈`实例化TV: 单个对象、对象数组
TV tv;
TV tv20[20];
//2. `从堆`实例化TV: 单个对象、对象数组
TV *t = new TV();
TV *t20 = new TV[20];
//3. 赋值,执行方法
tv.type = 20;
tv.name = "属性必须全值";
tv.power();
t->changeVol();
for (int i = 0; i < 20; i++) {
t20[i].type = i;
t20[i].name = "属性必须全值";
t20[i].power();
}
//4. `堆`不用清理:new => delete + null
delete t;
t = NULL;
delete []t20;
t20 = NULL;
//5. 类定义与使用,必须在main()之前(否则至少先声明)
Coordinate ct;
ct.x = 11;
ct.y = 33;
ct.printX();
ct.printY();
Coordinate *p = new Coordinate();
if (p == NULL){
//xxx
return 0;
}
p->x = 100;
p->y = 200;
p->printX();
p->printY();
delete p;
p = NULL;
//6. 字符串,定义与赋值
std::string s1;
std::string s2("Abc");
std::string s3(s2); //使用副本初始化
std::string s4(4, 'c'); //初始化字符的n个副本
std::cout << s1 << "*" << s2 << "+" << s3 << "~" << s4 << std::endl;
//6.1 字符串常用操作
s1.empty(); //true or false
s2.size(); //length
s2[2]; //索引为2的字符
s2 + s3; //拼接
s2 = s3; //替换
s2 == s3; //比较
s2 != s3;
2、类构造指针和系统堆、自定义栈
a.类构造函数、生命周期
1、面向对象
- 类的操作方法必须在其内部实现,如:直接吃肉和使用餐具;
- 除方法外,其他变量、类定义后需要分号结束
2、类外定义
- 类的方法写在类定义的外面,至于直接分开为 xx.h、xx.cpp;
- 类内定义的函数优先选择编译为内联函数(inline修饰),在编译阶段直接替换
(1)类内定义成员函数,可以不用在函数头部加inline关键字;
(2)类外定义:inline void TV::power(){};
(3)普通的宏定义函数:由预处理器对进行宏替代;
- 类文件调用,引用 class.cpp[内部引用class.h]
3、内存分区
- 栈区:存放变量类; [**自动管理**]
- 堆区:存放指针类; [手动管理]
- 全局区:存储全局变量、静态变量;
- 常量区:string str = "hello";
- 代码区:存放二进制代码;
4、构造函数
- 本名函数、没有类型修饰符,如果没有定义、系统自动定义空的
- 在类实例化时自动调用
- 重载
class Student
{
public:
Student(){m_strName = "Jim"} //无参构造函数
Student(string name){m_strName = name} //重载
};
- 列表赋值[构造函数初始化]
class Student
{
public:
Student():m_strName("Jim"),m_iAge(15){} //此列表先于构造函数执行
...
class Circle
{
public:
Circle():m_dPi(3.14){} //常量初始化:Circle类,定义Π
private:
const double m_dPi;
};
5、构造函数高级
- 拷贝构造函数:静态、类、引用变量,如果没有定义、系统自动定义空的
class Student
{
public:
Student(const Student &stu){} //拷贝构造函数
};
- 析构函数:对象销毁自动使用,无参、释放 new 的资源
class Student
{
public:
Student(const Student &stu){} //拷贝构造函数
~Student(){} //析构函数
};
- 对象生命历程:申请内存 - 初始化列表 - 构造函数 - 参与运算 - 析构函数 - 释放内存
b.类的指针,文件引用
1、类的构造引用
- &和指针运算
Coordinate coord[3]; //栈实例化,自动回收
coord[1].m_iX = 10;
Coordinate * p = new Coordinate[3]; //堆实例化
p[0].m_iY = 10;
p[1].m_iY = 111;
p[2].m_iY = 2282;
p->m_iY = 30; //覆盖p[0].m_iY //指针变量p的++相当于移位
cout << & (p[0]) << "\t" << & (p[1]) << "\t" << & (p[2]) << "\t" << & (p[3]) << endl;
cout << p++ << "\t" << p++ << "\t" << p++ << "\t" << p << endl; //结果同上,移位、下面不同
cout << & (p[0]) << "\t" << & (p[1]) << "\t" << & (p[2]) << "\t" << & (p[3]) << endl;
cout << p->m_iY << ", " << p[1].m_iY << endl; //随机数, 0
cout << --p << "\t" << --p << "\t" << --p << "\t" << p << endl; //指针复位,逆序输出
cout << p->m_iY << ", " << p[1].m_iY << endl
- 头部引用关联:属性头类、操作方法、主文件
关联实例:主类 + 线段类 + 坐标类
main_line.cpp
include "libs/Line.h";
libs/Line.cpp
include "Line.h";
libs/Line.h
include "Coordinate.h";
libs/Coordinate.cpp
include "Coordinate.h";
libs/Coordinate.h
-
a.主函数内引用xx.cpp或xx.h等效(编译工作),目前注意不要重复引用:"不重复"、<系统的方括号正常调用>,
b.操作方法xx.cpp调用属性头xx.h,
c.属性头类xx.h引用其它类,使用xx.cpp或xx.h,
d.列表赋值只定义一次 Line(int x1, int y1, int x2, int y2):pointA(x1,y1),pointB(x2,y2) {},建议放到操作方法中;
- 堆区销毁:[栈(数据结构):一种先进后出的数据结构]
Coordinate all init 1, 2
Coordinate all init 3, 4
Line all init
(1, 2) => (3, 4)
~ Line destory
~ Coordinate destory 3, 4
~ Coordinate destory 1, 2
- 浅拷贝、深拷贝:注意在有指针的对象拷贝时、重新分配空间,否则造成多次释放;
c.this、类的生命周期,常引用、常指针
1、this、类的生命周期,常引用、常指针
- this是对象的一个特殊的指针:Obj obj; this == & obj ;
Obj obj(10) ==> Obj obj(this, 10)
系统不会在多个对象错乱的情况,是为每个成员函数自动加了this指针;
- 对象生命周期[系统栈 分配空间]
Coordinate init (1, 2)
Coordinate init (3, 4)
Line (1, 2) => (3, 4) INIT ...
(1, 2) => (3, 4)
=====================
Coordinate init (11, 22)
Coordinate init (33, 44)
Line (11, 22) => (33, 44) INIT ...
const (11, 22) => (33, 44)
~ Line (11, 22) => (33, 44) DELETE
~ Coordinate destory 33, 44
~ Coordinate destory 11, 22
~ Line (1, 2) => (3, 4) DELETE
~ Coordinate destory 3, 4
~ Coordinate destory 1, 2
- 对象的常指针、常引用二,note1.txt继
//虚拟系统 this 首参的编译器工作:如果有内部函数、属性调用则自动进行[以区分位于不同对象]
a.const修饰*[指向]&[引用]符号时:[变量的:名-址-值 >>值不变]
CoordinateX coor(3,5);
const CoordinateX & coor2 = coor; // &coor2锁定 => coor锁定,coor2的内容锁定、可以有多个coor2定义
const CoordinateX * pCoor = & coor; // *pCoor锁定 => pCoor的地址不变、值可变,coor地址不可变、值可变
cout << "& coor2: " << &coor2 << "; * pCoor: " << pCoor << endl;
coor.info();
coor.setX(33); //coor地址不可变、值可变
cout << coor.getX() << endl;
//coor2.getY(); // error: 将'const CoordinateX'作为'this'参数丢弃限定词
//pCoor->getX(); // error: 同上
//coor2,pCoor set更不可能
CoordinateX coorP(22,33);
pCoor = & coorP; //pCoor的地址不变、值可变
b.const修饰变量时:[变量的:名-址-值 >>值不变]
CoordinateX door(3,5);
//CoordinateX & const door2 = door; // error: 'const'限定符不能应用于'CoordinateX&',[coor2无内容不可锁定]
CoordinateX * const pDoor = & door; // pCoor锁定 => pCoor指向[指针内容]不可变,coor1地址不可变、值可变
coor.info();
coor.setY(55);
cout << door.getY() << endl;
CoordinateX doorP(22,33);
//pDoor = & doorP; //error: 分配只读变量“pDoor”,[pCoor指向||指针内容不可变]
pDoor->setX(66);
cout << door.getX() << endl; //pDoor -> door