个人总结C++知识点

1、sizeof 运算符来计算 int, float, double 和 char 变量占用的空间大小
unsigned int n;
unsigned long long factorial = 1;

2、补码,反码,原码

  • 原码: 符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值
  • 反码: 正数的反码是其本身;负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
  • 补码: 正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1.

输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示
思路:
把一个整数减去1,再和原整数做与运算,会把整数最右边一个1变成0.那么一个整数的二进制表示中有多少个1,就可以进行多次这样的操作
避免死循环,我们可以不右移输入的数字n。首先把n和1做与运算,判断n的最低位是不是为1。接着把1左移一位得到2,再和n做与运算,就能判断n的次低位是不是1…….这样反复左移,每次都能判断n的其中一位是不是1

3、快速幂算法

比如求3999,我们可以把999分解为(512+256+128+64+32+4+2+1),相当于对999的二进制1111110111的各位都求幂然后再相乘
class Solution {
public:
double Power(double base, int exp) {
int p;
double res;
float tmp;
if(exp0)return 1;
if(base
0)return 0;
p = abs(exp);
res = 1;
tmp = base;
while§{
if(p&1){
res = restmp; //按位与运算,右移
}
p = p>>1;
tmp = tmp
tmp;
}
return exp<0?1/res:res;
}
};

4、const 常量折叠
所谓的常量折叠是编译器的一种优化技术,也就是代码编译时时 const 常量表达式直接替换成立即数。
不过需要注意的时,const 常量仍然会分配内存空间。

#include <iostream> 
using namespace std; 
int main(void) 
{ 
    const int a = 10; 
    int * p = (int *)(&a); 
    *p = 20; 
    cout<<"a = "<<a<<", *p = "<<*p<<endl; 
 
    int& c = const_cast<int&>(a);
    c = 20;
    cout << a<<"  "<< c<<endl;
     return 0; 
} 

输出的结果为:a的值是10,*p的值和c的值都是20。

5、柔性数组

所谓的柔性数组,是指结构体的最后一个成员的为长度为0的数组,这个数组本身不占结构体的内存,只是一个地址标记,相当于一个指针,标记结构体的结束地址和数组的起始地址。结合malloc 在堆上动态分配内存,相当于在结构体的末尾分配了动态数组,此之谓柔性。

struct XXX{
     int size;
     void data[0];
}

malloc(sizeof(XXX) + buff_len) , 便相当于结构体的末尾分配了一个 data[buff_len] 的数组。

6、无符号和有符号的比较

int i = -1;
unsigned j = 1;
j < i;      #表达式为真
while(a -b) #这个是死循环,永远为真,因为 不管a>b还是a<b,最后结果都是无符号数。无符号数永远大于0.
char i = -1;
unsigned char j = 1;
j < i;    //表达式为假

7、关键字和关键字出现的次数构成的pair来构造map可以模拟multiset
map的operator [] 运算:
如果关键字存在, 返回值是value 类型, 如果关键字不存在, 会将关键字插入, 返回返回插入的value 的引用, 对于类类型, 调用默认构造函数来初始化这个value, 对于内置类型, 初始化为0. 所以对于const 类型的map, 不能进行operator[]运算,防止修改数据.
注意map的元素是pair 类型, 且pair<const key_type, mapped_type>也就是.first 成员是个const 类型, 不能被修改.

8、C++11允许按照对象的值类型来重载

9、折半法查找
折半法查找判断循环结束的条件一定是low <= high, 一定要有=;最后才能从两个元素中锁定要high的那个,否则只能够锁定到low。(二分法看区间定义)

10、参数的入栈顺序
this指针不入栈,其他参数从右往左入栈,由于栈是向下增长的,所以可以通过左边的参数的地址推算右边参数的地址,这也是C语言中可变参数的原理。函数内部,按申明的顺序入栈,所以先定义的是大地址。

11、拷贝构造函数只能传引用,不能传值
传值,拷贝构造函数的调用时,实参到形参的传递就会调用拷贝构造函数,这样会陷入无休止的递归调用当中。

12、++a的值就是a的值,a++是个临时值(a本身的值再增加1).
参数的计算顺序从右往左,跟入栈的输入相同: 如果是临时变量,直接用临时变量的值代替临时变量;如果是左值,需要全部计算完,带入最终的值。

13.数字转字符串

1.std::string to_string(int value); //C++11
2.sprintf();    //低版本
3.stringstream.str();

14、C++保存小数位数要引用头文件#include ,在要保存的小数变量之前加上“fixed << setprecision(x)”表示保存x位有效数字
setiosflags(ios::fixed)<<setprecision(x):保存小数

格式化
输出时间格式(01:08:31)
#include “stdlib.h”
#include
#include
using namespace std;
int main(){
int a=1;
cout.setf(ios::right);
cout.fill(‘0’);
cout.width(2);
cout<<a<<endl;;
system(“pause”);
return 0;//01
}

15、cin在遇到空格、回车时,都会认为当前输入已经结束,自动切换到下一输入。

cin.getchar()可以读入

读入

string s; // 定义⼀个空字符串s
cin>>s;//不包括空格
getline(cin, s); // 读取⼀整⾏的字符串,包括空格

#include <stack>
stack<int> s;
s.empty() #如果栈为空返回true,否则返回false
s.size() #返回栈中元素的个数
s.pop() #删除栈顶元素但不返回其值
s.top() #返回栈顶的元素,但不删除该元素
s.push() #在栈顶压入新元素

e.g.

#include <iostream>
#include <stack>
using namespace std;
int main() {
    stack<int> s; // 定义⼀个空栈s
    for (int i = 0; i < 6; i++) {
        s.push(i); // 将元素i压⼊栈s中
    }
    cout << s.top() << endl; // 访问s的栈顶元素
    cout << s.size() << endl; // 输出s的元素个数
    s.pop(); // 移除栈顶元素
    return 0;
}

q.push(x):将x元素接到队列的末端;
q.pop() 弹出队列的第一个元素,并不会返回元素的值;
q.front()访问队首元
q.back()访问队尾元素
q.size()访问队中的元素个数

priority_queue<node> q;
q.empty() 如果队列为空,则返回true,否则返回false
q.size() 返回队列中元素的个数
q.pop() 删除队首元素,但不返回其值
q.top() 返回具有最高优先级的元素值,但不删除该元素
q.push() 在基于优先级的适当位置插入新元素

#include <iostream>
#include <set>
using namespace std;
int main() {
    set<int> s;
    s.insert(2);//向集合添加元素
    s.insert(3);//向集合添加元素
    cout << *(s.begin()) << endl; //输出第一个元素
    for (int i = 0; i < 10; i++) {//插入0 - 9
        s.insert(i);
    }
    for (set<int>::iterator it = s.begin(); it != s.end(); it++) {
        cout << *it << " ";//集合的遍历,it是一个迭代的指针
    }
    cout << endl << (s.find(2) != s.end()) << endl;//查找,元素
    s.erase(3);//删除元素
    cout << (s.find(3) != s.end()) << endl;
    return 0;
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
    map<string, string> m;
    m["hello"] = "world"; // 存储键为 "hello" 值为 "world"的键值对
    cout << m["hello"] << endl; // 访问map中key为"hello"的value,
    cout << m["world"] << endl;// 如果key不存在,则返回0或空
    m["world"] = "test"; // 将"world"键对应的值修改为3
    m["key"] = "value"; // 设置键为 "key" 值为"value" 的键值对
    //迭代器遍历,输出map中所有的元素,键it->first获取,值it->second获取
    for (map<string, string>::iterator it = m.begin(); it != m.end(); it++) {
        cout << it->first << " " << it->second << endl;
    }
    cout << m.begin()->first << " " << m.begin()->second << endl;
    // 访问map的第一个元素,输出它的键和值
    cout << m.rbegin()->first << " " << m.rbegin()->second << endl;
    // 访问map的最后一个元素,输出它的键和值
    cout << m.size() << endl;
    // 输出map的元素个数
    return 0;
}

vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。

vectortest; // 定义的时候不指定vector的⼤⼩
vectortest2(6); // 定义的时候指定vector的⼤⼩,默认test2⾥⾯元素都是0
vectortest3(6, 3); // 定义的时候指定vector的⼤⼩,默认test3⾥⾯元素都是3

尾部添加两个元素,0的位置存储5, 1 的位置存储8
test.push_back(5);
test.push_back(8);

test.insert(test.begin()+i,value);//在第i+1个元素前面插入value;
test.erase(test.begin() + 5);//删除第6个元素
test.begin();//起始地址
test.end();//结束地址
test.clear();
test.size();
sort(test.begin(),test.end());
reverse(test.begin(),test.end());//反转
遍历

for (vector<int>test::iterator it = m.begin(); it != m.end(); it++) {
        cout << *it << endl;
    }
#include <iostream>
#include <vector>
using namespace std;

vector<int> dynamicArray() {
    int n = 0;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    return a;
}
int main() {
    //输入数组元素时,回车与空格相同
    vector<int> a = dynamicArray();

    return 0;
}

动态数组的优点就是静态数组的缺点改正以后:
1、运行时创建,不必事先规定大小
2、数组越界会被debug到
3、可以作为函数返回值

STL六大部件
容器 container:存储数据,用户不必关心数据在内存的存储形式,只需要知道怎么用容器接口
分配器 allocator:帮助容器在内存规划位置
算法 algorithm:与数据分离,能够执行某种操作
迭代器 iterator:连接容器(数据)和算法(操作)的桥梁,好像一种泛化指针
适配器 adapter:进行转换,可以针对容器、仿函数、迭代器
仿函数 Funtor:作用像一个函数
标准库规定,容器的头尾是前闭后开,即[ )类型

容器分类
序列式容器:线性排列≠大小排序
array:由C++本身的数组包装得到。大小固定,不能改变
vector:能够自动扩充的数组,只能在尾部扩充
deque:双向队列,可以在头尾扩充
–V&D访问效率高 在尾部增加或删除元素的效率最高(时间复杂度为 O(1) 常数阶),在其它位置插入或删除元素效率较差(时间复杂度为 O(n) 线性阶,其中 n 为容器中元素的个数)

list:双向链表
–任何地方都可以高效地增加或删除元素(时间复杂度都为常数阶 O(1)),但访问容器中任意元素的速度要比前三种容器慢,时间复杂度为 O(n) 线性阶
forward-list:单向链表
stack 和 queue 本质上也属于序列容器,只不过它们都是在 deque 容器的基础上改头换面而成,通常更习惯称它们为容器适配器

#include <list>

和array、vector、deque 容器的迭代器相比,list 容器迭代器最大的不同在于,其配备的迭代器类型为双向迭代器【链表】,而不再是随机访问迭代器。
支持使用 ++p1、 p1++、 p1–、 p1++、 *p1、 p1==p2 以及 p1!=p2 运算符,但不支持以下操作(其中 i 为整数):
p1[i]:不能通过下标访问 list 容器中指定位置处的元素。
p1-=i、 p1+=i、 p1+i 、p1-i:双向迭代器 p1 不支持使用 -=、+=、+、- 运算符。
p1<p2、 p1>p2、 p1<=p2、 p1>=p2:双向迭代器 p1、p2 不支持使用 <、 >、 <=、 >= 比较运算符。

erase() 成员函数是按照被删除元素所在的位置来执行删除操作,如果想根据元素的值来执行删除操作,可以使用 remove() 成员函数。remove(aim)是删除链表中的aim元素,若有多个aim,都会删除

unique() 去重

emplace() 每次只能插入一个元素,而不是多个

#include<algorithm> :remove swap()
remove() 函数删除掉 demo 容器中的多个指定元素,该容器的大小和容量都没有改变,其剩余位置还保留了之前存储的元素。我们可以使用 erase() 成员函数删掉这些 “无用” 的元素。remove()用于删除容器中指定元素时,常和 erase() 成员函数搭配使用。
在调用erase()或clear()删除元素后,vector的size()确实减小了,但是被删除元素原先占用的内存却并没有得到释放,不进行释放的话,可能会造成内存上的浪费。使用swap()能够在不析构vector的前提下,释放多余的内存。

#include <deque>

和 vector 相比,额外增加了实现在容器头部添加和删除元素的成员函数,同时删除了 capacity()、reserve() 和 data() 成员函数。
如begin()、迭代器的功能是遍历容器,在遍历的同时可以访问(甚至修改)容器中的元素,但迭代器不能用来初始化空的 deque 容器。对于空的 deque 容器来说,可以通过 push_back()、push_front() 或者 resize() 成员函数实现向(空)deque 容器中添加元素。

关联式容器(有序)
有key和value,十分适合需要大量查找的数据

set/multiset:底层红黑树。key就是value,即红黑树的每个节点上只存储一个key值,该key值就=value值。
set表示存放元素的key不能重复,multiset表示存放元素的key可以重复
#include <set>

set 容器类模板中未提供 at() 成员函数,也未对 [] 运算符进行重载。
双向迭代器:假设 p 为此类型的迭代器,则其只能进行 ++p、p++、–p、p–、*p 操作,并且 2 个双向迭代器之间做比较,也只能使用 == 或者 != 运算符。

map/multimap:底层红黑树。key和value分开存放,即红黑树的每个节点上既要存储key值,还要存储value值。
map表示存放元素的key不能重复(键值不能修改),multimap表示存放元素的key可以重复
以红黑树为底层的关联式容器,key值都是有序存放的,因为红黑树本身就是有序的
#include <map>
在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。降序:std::greater
e.g. map<string, int, greater >myMap{ {“C语言教程”,10},{“STL教程”,20} };

Map访问:[ ]或者at()
但multimap 未提供 at() 成员方法,也没有重载 [] 运算符。

无序式容器
其实也是一种关联式容器,但是独立了出来
unordered set/multiset:底层哈希表
unordered map/multimap:底层哈希表

C++ STL 底层采用哈希表实现无序容器时,会将所有数据存储到一整块连续的内存空间中,并且当数据存储位置发生冲突时,解决方法选用的是“链地址法”(又称“开链法”)。
1、无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键
2、和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。

#include<unordered_map>

通过 find() 方法得到的是一个正向迭代器,该迭代器的指向分以下 2 种情况:
当 find() 方法成功找到以指定元素作为键的键值对时,其返回的迭代器就指向该键值对;
当 find() 方法查找失败时,其返回的迭代器和 end() 方法返回的迭代器一样,指向容器中最后一个键值对之后的位置。

unordered_set容器模板类中没有重载 [ ] 运算符,也没有提供 at() 成员方法。不仅如此,由于容器内部存储的元素值不能被修改,因此无论使用那个迭代器方法获得的迭代器,都不能用于修改容器中元素的值。

C++11新规定的遍历方式

for (decl : coll) {
    statement
}
//decl是declare,声明一个变量
//coll是collection,表示一个元素集合/容器

//示例1:遍历集合中的元素
for (int i :{2,3,4,5}) {
    cout << i << endl;
}
//示例2:传值输出集合中的元素
for (auto e : vec) {
    cout << e << endl;
}
//示例3:传址修改集合中的元素
for (auto& e : vec) {
    e *= 3;
}

迭代器
常用的迭代器按功能强弱分为输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器 5 种。常用的就是后三种,输入迭代器和输出迭代器比较特殊,它们不是把数组或容器当做操作对象,而是把输入流/输出流作为操作对象。

前向迭代器(forward iterator):
假设 p 是一个前向迭代器,则 p 支持 ++p,p++,*p 操作,还可以被复制或赋值,可以用 == 和 != 运算符进行比较。此外,两个正向迭代器可以互相赋值。

双向迭代器(bidirectional iterator)
双向迭代器具有正向迭代器的全部功能,除此之外,假设 p 是一个双向迭代器,则还可以进行 --p 或者 p-- 操作(即一次向后移动一个位置)。

随机访问迭代器(random access iterator)
随机访问迭代器具有双向迭代器的全部功能。除此之外,假设 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:

p+=i:使得 p 往后移动 i 个元素。
p-=i:使得 p 往前移动 i 个元素。
p+i:返回 p 后面第 i 个元素的迭代器。
p-i:返回 p 前面第 i 个元素的迭代器。
p[i]:返回 p 后面第 i 个元素的引用。
此外,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。另外,表达式 p2-p1 也是有定义的,其返回值表示 p2 所指向元素和 p1 所指向元素的序号之差(也可以说是 p2 和 p1 之间的元素个数减一)。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值