目录
1.仿函数重置set排序规则
class mycompare {
public:
bool operator()(int a, int b) const {
return a > b;
}
};
int main() {
set<int, mycompare> s{ 3,4,2 };//默认是set<int,less<>>s; 大端
auto it = s.begin();
for (; it!=s.end(); it++)
{
cout << *it << " ";
}
}
2,修改is_sorted()排序规则
bool mysort( int &a, int &b) {
return a > b;
}
bool test(vector<int>&v) {
return is_sorted(v.begin(), v.end(),mysort);
}
int main() {
vector<int>v{1,2,3};
cout<<test(v);
}
不加mysort是非递减排序,自定义后,非递增排序
3.关于const修饰函数
1.函数后面加const,const修饰的是this和(*this),比如
public:
void add(int a, int b)const//OK 其实这是传入的this指针应该是这样的形式:
// const A * const this 所以this 和*this 都不允许改变
{
m_c = 100;//ok
m_a = 100;//
this = 0XFFFF0048;//error
}
2.加了const的成员函数可以被非const对象和const对象调用
3.不加const的成员函数只能被非const对象调用
4.只允许在类的非静态成员函数后面加const
比如:
class A
{
private:
int data;
public:
A(int num):data(num){}
~A(){};
int& get_data()
{
return data;
}
};
int main()
{
const A a(1);
a.get_data();
return 0;
}
报错 因为a是const对象,但是成员函数中的this是非const
#1:this指针是什么时候创建的?
this在成员函数的开始执行前构造的,在成员的执行结束后清除。
#2:this指针存放在何处? 堆,栈,全局变量,还是其他?
this指针会因编译器不同,而放置的位置不同。可能是栈,也可能是寄存器,甚至全局变量。
3.accumulate函数
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main() {
vector<int> arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum = accumulate(arr.begin(), arr.end(), 0); // 初值0 + (1 + 2 + 3 + 4 +... + 10)
cout << sum << endl; // 输出55
return 0;
}
4.关于多文件编译
gcc a.c main.c -o app -I [头文件目录]
1.对于main.c 中include了头文件,有了函数声明,使得编译通过形成.o文件
2.a.c可以不包含头文件也可以编译
3.链接阶段,1中的.o文件才去找2的.o文件,形成最终的可执行程序
a.c
#include <stdio.h>
#include <head.h>
int add(int a, int b)
{
return a+b;
}
main.c
#include <stdio.h>
#include <head.h>
int main()
{
int a = 20;
int b = 12;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
return 0;
}
head.h
#ifndef _HEAD_H
#define _HEAD_H
int add(int a, int b);
#endif
5.sizeof,strlen
-
sizeof是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen是字符处理的库函数。
-
sizeof参数可以是任何数据的类型或者数据(sizeof参数不退化);strlen的参数只能是字符指针且结尾是'\0'的字符串。
6.share_ptr智能指针循环引用
当两个类中定义的share智能指针互相指向对方对象的时候,因为是在堆空间开辟的内存,栈空间的指针会在生命周期结束后析构并销毁,而堆空间还是存在引用计数,此时无法销毁堆空间的对象 。解决方案,在m_person指针定义的时候用weak_ptr,不会增加引用计数。
7.纯虚函数和抽象类
8.右值引用相关
1.作用,实现移动语义和完美转发
2.声明出来的左值引用或者右值引用都是左值
3.右值引用通过std::move(v)可以指向左值 ,v从左值变成右值
移动语义:
因为复制拷贝构造是无法改变对象的指向的,用移动拷贝构造可以改变对象指针指向。
可知a.m_data需要指向nullptr,不能用const 且对象 a是左值,move(a)后变右值然后传参即可实现移动构造
9.拷贝复制构造和移动拷贝构造
移动拷贝构造触发时机: 如果临时对象即将消亡,并且它里面的资源是需要被再利用的,这个时候我们就可以触发移动构造
区别:
10.c_str()
C++ string 类型转C的const char* 类型,返回字符数组首字符地址。
11.关于会话
12.虚函数,析构函数重点
1.构造函数一定不能是虚函数。
vtab是在编译阶段形成的,vptr是在程序运行的时候形成,如果构造函数是虚函数的话,编译阶段存在vtab里面,导致实例化对象的时候无法调用构造函数,从而无法形成vptr。
2.父类析构函数一定是虚函数。
如果父类析构函数不是虚函数,main中如果animal* p = new cat();delete p;
此步只能析构父类创建的内存空间,子类没调用析构。会导致内存泄漏。
13.右值引用注意事项
int num = 10;
int &b = num; √ // num是左值,可以左值建立引用
int &b = 10; × // 10是右值,不可以右值建立引用 但可以const加前面就行
左值可以引用,拷贝一份,但是右值不能引用,那如果要引用呢,C++11有了对右值的引用如下下。
左值引用 对待左值,右值引用对待右值
可以取地址的为左值,反之右值
int a = 3; // a是左值,3是右值
int d = a; // d和a都是左值,左值可以赋给左值右值引用需要初始化;
int &&d = a; × // 右值引用左值不行
int &&d = 10; // 右值引用右值可以
int &&d = f(10); // 右值引用右值可以
14.隐式构造
A b = 123:
1)A tmp(123),将123隐式转换为A(调用构造函数),创建临时对象tmp。
2)A b=tmp,调用拷贝构造函数,完成赋值。如果对构造函数加explicit,就不能隐式构造
下面这类也是隐式构造
但是explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的, 所以explicit关键字也就无效了
string str3 = "I am a string"
class CxString // 使用关键字explicit的类声明, 显示转换
{
public:
char *_pstr;
int _size;
explicit CxString(int size)
{
_size = size;
// 代码同上, 省略...
}
CxString(const char *p)
{
// 代码同上, 省略...
}
};
// 下面是调用:
CxString string1(24); // 这样是OK的
CxString string2 = 10; // 这样是不行的, 因为explicit关键字取消了隐式转换
CxString string3; // 这样是不行的, 因为没有默认构造函数
CxString string4("aaaa"); // 这样是OK的
CxString string5 = "bbb"; // 这样也是OK的, 调用的是CxString(const char *p)
CxString string6 = 'c'; // 这样是不行的, 其实调用的是CxString(int size), 且size等于'c'的ascii码, 但explicit关键字取消了隐式转换
string1 = 2; // 这样也是不行的, 因为取消了隐式转换
string2 = 3; // 这样也是不行的, 因为取消了隐式转换
string3 = string1; // 这样也是不行的, 因为取消了隐式转换, 除非类实现操作符"="的重载
15.关于为什么不能返回局部变量的引用或指针
16.结构体对齐
17.八股们
static:
- 分文件操作的时候,静态变量(函数)只能在当前文件使用,不能通过extern在多个文件使用
- 局部变量未初始化的时候值未定,存在栈区,静态变量未初始化的时候默认为0,存储在全局区。
- 类内静态成员变量,类内声明,类外定义,每个对象共有这个成员变量。
- 类静态成员函数没有this指针,不能调用非静态成员变量和函数。
《分文件》《储存》《类中变量》《类中函数》
Const:
- 是常量限定符,指定定义的哪些量为常量
- 修饰变量是表示变量不可变,为常量
- 修饰指针的时候分为常量指针和指针常量,常量指针指的是const在*号前,表示指针指向的变量的值不可变,指针常量表示const在*后,表示指针的指向不可变。
- 修饰函数的时候,类成员函数中,const表示this的指向和this不可变,相当于类对象的成员变量不能变,增加程序健壮性。
- 修饰函数返回值和函数形参的效果都是为了指定返回值和形参为常量,作为保护。
《定义》《修饰变量》《修饰指针》《修饰函数》《修饰形参和返回值》
new / delete 与 malloc / free
- 相同点:都可用于内存的动态申请和释放
- 前者是C++ 运算符支持重载, 后者是 c/c++标准库函数
- 前者可以自动计算分配的空间大小,后者手动,以及返回指针的不同。
- new调用名为operator new的标准库函数分配足够空间并调用相关对象的构造函数,delete对指针所指对象运行适当的析构函数,然后通过调用名为operator delete的标准库函数释放该对象所用内存。后者均没有相关调用
- new是封装了malloc,直接free不会报错,但是这只是释放内存,而不会析构对象
- Newdelete的好处,对于非基本数据类型的对象,需要执行构造函数析构函数。
18.缺省构造函数
#include<iostream>
using namespace std;
class Test
{
public:
//Test()//缺省构造函数
//{
// data = 0;
// cout << "缺省构造函数,初始化数据" << this->data << endl;
//}
Test(int a=2)//缺省构造函数
{
this->x = a;
cout << "带一个参数的构造函数,初始化数据" << this->x << endl;
}
private:
int x;
int data;
};
int main()
{
Test t;
return 0;
}
打开注释运行报错,缺省构造(要么无参 要么有参但初始化)只能一个
19.单例模式创建类
删除默认的拷贝构造和赋值运算符重载函数,让默认构造函数进私有函数里面,只允许类内成员访问,在私有区域内创建指针声明,类外定义指针指向的对象,注意,只能由该指针创建对象,且用public中的函数访问private,因为类外只能访问public中的函数,main中新建该类指针,指向private里面指针指向的对象,完成单例创建,且无法创建其他实例类。注意:
1.静态函数只能访问静态变量(没有this指针,无法操控成员变量),非成员函数也可以访问静态变量
2.静态成员和函数只能通过类名访问
3.静态变量类内声明,类外实现。
20.信号量和互斥锁和条件变量
1.信号量对进程和线程,互斥锁和条件变量对线程
2.信号量有定量的值,可以对不同线程PV(加锁解锁)操作,P(>0 -- ,==0 wait) V(++),互斥锁对同一线程加锁解锁.
3.条件变量可以一次唤醒所有等待者,信号量不行