C++ 学习笔记

C++基础(一些细节)

  1. C++ 中使用 cout 输出 bool 变量的值时还是用数字 1 和 0 表示,而不是 true 或 false。(C++ 中 非0即1(只用数值0/false是0,其他全是1))

  2. new 和 delete、new[] 和 delete[] 操作符应该成对出现,并且不要和C语言中 malloc()、free() 一起混用。

  3. 为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于C语言中的宏展开。这种在函数调用处直接嵌入函数体的函数称为内联函数(Inline Function),又称内嵌函数或者内置函数。(在函数前加上关键字 inline 即可(要在函数定义处添加 inline 关键字,在函数声明处添加 inline 关键字虽然没有错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。))

  4. 当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,所以我们一般是将非常短小的函数声明为内联函数。

  5. C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。实参和形参的传值是从左到右依次匹配的,默认参数的连续性是保证正确传参的前提。

// 正确
void func(int a, int b=10, int c=20){ }
void func(int a, int b, int c=20){ }
// 错误
void func(int a, int b=10, int c=20, int d){ }
void func(int a, int b=10, int c, int d=20){ }
  1. 参数列表又叫参数签名,包括参数的类型、参数的个数和参数的顺序,只要有一个不同就叫做参数列表不同。
  2. 类名的首字母一般大写,以和其他的标识符区分开。{ }内部是类所包含的成员变量和成员函数,它们统称为类的成员
class Student{public://成员变量char *name;int age;float score;//成员函数void say(){cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;}
};Student liLei;  //创建对象
  1. 两种创建对象的方式:一种是在栈上创建,形式和定义普通变量类似;另外一种是在堆上使用 new 关键字创建,必须要用一个指针指向它,读者要记得 delete 掉不再使用的对象。
  2. 对象名字访问成员使用点号.,通过对象指针访问成员使用箭头->,这和结构体非常类似。
//创建对象Student stu;stu.name = "小明";stu.age = 15;stu.score = 92.5f;stu.say();Student *pStu = new Student;pStu -> name = "小明";pStu -> age = 15;pStu -> score = 92.5f;pStu -> say();delete pStu;  //删除对象
  1. 也可以只在类体中声明函数,而将函数定义放在类体外面(::被称为域解析符(也称作用域运算符或作用域限定符),用来连接类名和函数名,指明当前函数属于哪个类)(成员函数必须先在类体中作原型声明,然后在类外定义,也就是说类体的位置应在函数定义之前。)
class Student{public://成员变量char *name;int age;float score;//成员函数void say();  //函数声明
};
//函数定义
void Student::say(){cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
  1. 在类体中和类体外定义成员函数是有区别的:在类体中定义的成员函数会自动成为内联函数,在类体外定义的不会。当然,在类体内部定义的函数也可以加 inline 关键字,但这是多余的,因为类体内部定义的函数默认就是内联函数。
  2. 内联函数一般不是我们所期望的,它会将函数调用处用函数体替代,所以我建议在类体内部对成员函数作声明,而在类体外部进行定义,这是一种良好的编程习惯,实际开发中大家也是这样做的。当然,如果你的函数比较短小,希望定义为内联函数,那也没有什么不妥的。
  3. 类函数定义在外部也可以是内联函数
class Student{public:char *name;int age;float score;void say();  //内联函数声明,可以增加 inline 关键字,但编译器会忽略
};
//函数定义
inline void Student::say(){cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
  1. 构造函数在实际开发中会大量使用,它往往用来做一些初始化工作,例如对成员变量赋值、预先打开文件等。
  2. 调用没有参数的构造函数也可以省略括号
  3. VLA 类包含了两个成员变量,m_len 和 m_arr 指针,需要注意的是 m_len 加了 const 修饰,只能使用初始化列表的方式赋值
class VLA{private:const int m_len;int *m_arr;
public:VLA(int len);
};
//必须使用初始化列表来初始化 m_len
VLA::VLA(int len): m_len(len){m_arr = new int[len];
}

17.如下赋值是错误的

class VLA{private:const int m_len;int *m_arr;
public:VLA(int len);
};
VLA::VLA(int len){m_len = len;m_arr = new int[len];
}
  1. 上面我们定义了一个 VLA 类来模拟变长数组,它使用一个构造函数为数组分配内存,这些内存在数组被销毁后不会自动释放,所以非常有必要再添加一个析构函数,专门用来释放已经分配的内存。请看下面的完整示例:
#include <iostream>
using namespace std;
class VLA{public:VLA(int len);  //构造函数~VLA();  //析构函数
public:void input();  //从控制台输入数组元素void show();  //显示数组元素
private:int *at(int i);  //获取第i个元素的指针
private:const int m_len;  //数组长度int *m_arr; //数组指针int *m_p;  //指向数组第i个元素的指针
};
VLA::VLA(int len): m_len(len){  //使用初始化列表来给 m_len 赋值if(len > 0){ m_arr = new int[len];  /*分配内存*/ }else{ m_arr = NULL; }
}
VLA::~VLA(){delete[] m_arr;  //释放内存
}
void VLA::input(){for(int i=0; m_p=at(i); i++){ cin>>*at(i); }
}
void VLA::show(){for(int i=0; m_p=at(i); i++){if(i == m_len - 1){ cout<<*at(i)<<endl; }else{ cout<<*at(i)<<", "; }}
}
int * VLA::at(int i){if(!m_arr || i<0 || i>=m_len){ return NULL; }else{ return m_arr + i; }
}
int main(){//创建一个有n个元素的数组(对象)int n;cout<<"Input array length: ";cin>>n;VLA *parr = new VLA(n);//输入数组元素cout<<"Input "<<n<<" numbers: ";parr -> input();//输出数组元素cout<<"Elements: ";parr -> show();//删除数组(对象)delete parr;return 0;
}
  1. 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
class Student{public:Student(char *name, int age, float score);void show();
public:static int m_total;  //静态成员变量
private:char *m_name;int m_age;float m_score;
};
/通过类类访问 static 成员变量
Student::m_total = 10;
//通过对象来访问 static 成员变量
Student stu("小明", 15, 92.5f);
stu.m_total = 20;
//通过对象指针来访问 static 成员变量
Student *pstu = new Student("李华", 16, 96);
pstu -> m_total = 20;
  1. static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
  2. 静态成员变量必须初始化,而且只能在类体外进行。例如:
int Student::m_total = 10;
  1. 友元函数可以访问当前类中的所有成员,包括 public、protected、private 属性的
public:friend void show(Student *pstu);  //将show()声明为友元函数//非成员函数
void show(Student *pstu){cout<<pstu->m_name<<"的年龄是 "<<pstu->m_age<<",成绩是 "<<pstu->m_score<<endl;Student stu("小明", 15, 90.6);show(&stu);  //调用友元函数Student *pstu = new Student("李磊", 16, 80.5);show(pstu);  //调用友元函数
}
  1. 注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。下面的写法是错误的:
void show(){cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
}
  1. 成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
  2. friend 函数不仅可以是全局函数(非成员函数),还可以是另外一个类的成员函数。请看下面的例子:
#include <iostream>
using namespace std;
class Address;  //提前声明Address类
//声明Student类
class Student{public:Student(char *name, int age, float score);
public:void show(Address *addr);
private:char *m_name;int m_age;float m_score;
};
//声明Address类
class Address{private:char *m_province;  //省份char *m_city;  //城市char *m_district;  //区(市区)
public:Address(char *province, char *city, char *district);//将Student类中的成员函数show()声明为友元函数friend void Student::show(Address *addr);
};
//实现Student类
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(Address *addr){cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;cout<<"家庭住址:"<<addr->m_province<<"省"<<addr->m_city<<"市"<<addr->m_district<<"区"<<endl;
}
//实现Address类
Address::Address(char *province, char *city, char *district){m_province = province;m_city = city;m_district = district;
}
int main(){Student stu("小明", 16, 95.5f);Address addr("陕西", "西安", "雁塔");stu.show(&addr);Student *pstu = new Student("李磊", 16, 80.5);Address *paddr = new Address("河北", "衡水", "桃城");pstu -> show(paddr);return 0;
}
  1. 与C风格的字符串不同,string 的结尾没有结束标志’\0’
  2. string 一些常用函数
s.length() //字符串长度
// 插入字符串
s1 = s2 = "1234567890";s3 = "aaa";s1.insert(5, s3);
// 删除字符串
// string& erase (size_t pos = 0, size_t len = npos);
// pos 表示要删除的子字符串的起始下标,len 表示要删除子字符串的长度。如果不指明 len 的话,那么直接删除从 pos 到字符串结束处的所有字符(此时 len = str.length - pos)
string s3 = "1234567890"
s2.erase(5);   //12345
s3.erase(5, 3);   //1234590// 提取子字符串
// string substr (size_t pos = 0, size_t len = npos) const;
// pos 为要提取的子字符串的起始下标,len 为要提取的子字符串的长度
string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6);  //s2: second
// 字符串查找函数
// size_t find (const string& str, size_t pos = 0) const;
// size_t find (const char* s, size_t pos = 0) const;
// 第一个参数为待查找的子字符串,它可以是 string 字符串,也可以是C风格的字符串。第二个参数为开始查找的位置(下标);如果不指明,则从第0个字符开始查找。
string s1 = "first second third";
string s2 = "second";
int index = s1.find(s2,5);
if(index < s1.length())cout<<"Found at index : "<< index <<endl;
elsecout<<"Not found"<<endl;
// 返回结果是Found at index:6
// rfind() 函数
// rfind() 和 find() 很类似,同样是在字符串中查找子字符串,不同的是 find() 函数从第二个参数开始往后查找,而 rfind() 函数则最多查找到第二个参数处,如果到了第二个参数所指定的下标还没有找到子字符串,则返回一个无穷大值4294967295。
int main(){string s1 = "first second third";string s2 = "second";int index = s1.rfind(s2,6);if(index < s1.length())cout<<"Found at index : "<< index <<endl;elsecout<<"Not found"<<endl;return 0;
}
// 返回 Found at index : 6
int main(){string s1 = "first second second third";string s2 = "asecond";int index = s1.find_first_of(s2);if(index < s1.length())cout<<"Found at index : "<< index <<endl;elsecout<<"Not found"<<endl;return 0;
}
//返回 Found at index : 3
  1. 引用(Reference)是 C++ 相对于C语言的又一个扩充。引用可以看做是数据的一个别名,通过这个别名和原来的名字都能够找到这份数据。引用类似于 Windows 中的快捷方式,一个可执行程序可以有多个快捷方式,通过这些快捷方式和可执行程序本身都能够运行程序;引用还类似于人的绰号(笔名),使用绰号(笔名)和本名都能表示一个人。
  2. 引用的定义方式类似于指针,只是用&取代了*,语法格式为:
// 语法
// type &name = data;#include <iostream>
using namespace std;
int main() {int a = 99;int &r = a;cout << a << ", " << r << endl;      //99,99cout << &a << ", " << &r << endl;    //0x5267ff,0x5267ffreturn 0;
}
  1. 注意,引用在定义时需要添加&,在使用时不能添加&,使用时添加&表示取地址。如上面代码所示,第 8 行中的&表示引用,第 10 行中的&表示取地址。除了这两种用法,&还可以表示位运算中的与运算。
  2. 由于引用 r 和原始变量 a 都是指向同一地址,所以通过引用也可以修改原始变量中所存储的数据
int a = 99;
int &r = a;
r = 47;
  1. 引用除了可以作为函数形参,还可以作为函数返回值。在将引用作为函数返回值时应该注意一个小问题,就是不能返回局部数据(例如局部变量、局部对象、局部数组等)的引用,因为当函数调用完成后局部数据就会被销毁,有可能在下次使用时数据就不存在了,C++ 编译器检测到该行为时也会给出警告。
  2. 下面我们定义一个基类 People,然后由此派生出 Student 类:
#include<iostream>
using namespace std;//基类People
class People{public:void setname(char *name);void setage(int age);char *getname();int getage();
private:char *m_name;int m_age;
};
void People::setname(char *name){m_name = name;}
void People::setage(int age){m_age = age;}
char* People::getname(){return m_name;}
int People::getage(){return m_age;}// 派生类 Student
class Student: public Peolpe{public:void setscore(float score);float getscore();
private:float m_score;
};
void Student::setscore(float score){ m_score = score; }
float Student::getscore(){ return m_score; }
int main(){Student stu;stu.setname("小明");stu.setage(16);stu.setscore(95.5f);cout<<stu.getname()<<"的年龄是 "<<stu.getage()<<",成绩是 "<<stu.getscore()<<endl;return 0;
}
  1. 由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。

C++学习笔记(自己用的)相关推荐

  1. PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 call

    您的位置 首页 PyTorch 学习笔记系列 PyTorch 学习笔记(六):PyTorch hook 和关于 PyTorch backward 过程的理解 发布: 2017年8月4日 7,195阅读 ...

  2. 容器云原生DevOps学习笔记——第三期:从零搭建CI/CD系统标准化交付流程

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  3. 容器云原生DevOps学习笔记——第二期:如何快速高质量的应用容器化迁移

    暑期实习期间,所在的技术中台-效能研发团队规划设计并结合公司开源协同实现符合DevOps理念的研发工具平台,实现研发过程自动化.标准化: 实习期间对DevOps的理解一直懵懵懂懂,最近观看了阿里专家带 ...

  4. 2020年Yann Lecun深度学习笔记(下)

    2020年Yann Lecun深度学习笔记(下)

  5. 2020年Yann Lecun深度学习笔记(上)

    2020年Yann Lecun深度学习笔记(上)

  6. 知识图谱学习笔记(1)

    知识图谱学习笔记第一部分,包含RDF介绍,以及Jena RDF API使用 知识图谱的基石:RDF RDF(Resource Description Framework),即资源描述框架,其本质是一个 ...

  7. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记

    计算机文化基础(第十讲)学习笔记 采样和量化PictureElement Pixel(像素)(链接: 采样的实质就是要用多少点(这个点我们叫像素)来描述一张图像,比如,一幅420x570的图像,就表示 ...

  8. Go 学习推荐 —(Go by example 中文版、Go 构建 Web 应用、Go 学习笔记、Golang常见错误、Go 语言四十二章经、Go 语言高级编程)

    Go by example 中文版 Go 构建 Web 应用 Go 学习笔记:无痕 Go 标准库中文文档 Golang开发新手常犯的50个错误 50 Shades of Go: Traps, Gotc ...

  9. MongoDB学习笔记(入门)

    MongoDB学习笔记(入门) 一.文档的注意事项: 1.  键值对是有序的,如:{ "name" : "stephen", "genda" ...

  10. NuGet学习笔记(3) 搭建属于自己的NuGet服务器

    文章导读 创建NuGetServer Web站点 发布站点到IIS 添加本地站点到包包数据源 在上一篇NuGet学习笔记(2) 使用图形化界面打包自己的类库 中讲解了如何打包自己的类库,接下来进行最重 ...

最新文章

  1. 快手用旺旺瓶子做机器人_用平底锅做西多士,早餐不发愁,孩子三天两头点名吃,简单快手...
  2. python自带intertool模块找不到_Python itertools模块:生成迭代器(示例分析)
  3. 安装工程造价课程设计_造价课程设计.docx
  4. arch linux引导不启动_Linux 内核源代码的目录结构
  5. seo发展基本趋势优化专员必须知道!
  6. 10个精选的颜色选择器Javascript脚本及其jQuery插件
  7. Linux登录界面消失解决办法
  8. Python sqrt() 函数
  9. 三种方式读取项目属性文件
  10. 你必须收藏的 GitHub 技巧
  11. python opencv保存图片到指定路径_OpenCV-将图像保存到所选的特定文件夹
  12. 详解:MapReduce 思想解析
  13. php执行另一个页面,从另一个PHP脚本执行PHP脚本
  14. oracle取本月最后一天是星期几_在oracle里,如何取得本周、本月、本季度、本年度的第一天和最后一天的时间...
  15. git提交了不需要的文件夹或者文件怎么办
  16. 如何在jdk官网下载想要的版本
  17. VScode 英文翻译成中文插件(英语差的福音)
  18. python逆时针旋转矩阵_由外向内顺时针逆时针旋转矩阵
  19. SATA-AHCI规范学习
  20. 记一次突然宕机重启服务器导致docker中redis无法启动的问题解决

热门文章

  1. 打骚扰电话的骗子,居然被人骚扰成了这样……
  2. cyq.data 连接mysql_CYQ.Data 轻量数据层之路 使用篇一曲 裸身走天涯(十二)
  3. [深入理解Java虚拟机]第六章 Class类文件的结构
  4. 某店私信Websocket分析学习
  5. 写作小技能:Media Platform 类型定位
  6. 360n6能装原装android,降价之后,官方确认360 N6 Pro升级安卓8.0
  7. selenium PO模式
  8. 天载配资点评指数/情绪/方向
  9. linux中yum安装word软件libreoffice
  10. 长达8.21万公里公路在青海高原延展 可绕地球赤道两周