基本语法

auto:在C++中,auto可以自动推导类型。

1
2
3
4
5
#include <vector>
std::vector <int> v;
//有两种方式可以遍历STL容器
- for (std::vector::iterator it = v.begin();it!=v.end();it++)
- for (auto vtest : v)

&为引用,加&与否取决于是否要修改原值或避免拷贝

  • 需要修改并输出修改后的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>

std::vector <int> v{0,1,2,3,4,5};

int main(){
for (auto &v1:v)
{
v1 = v1+1;
}
for (auto v1:v)
{
std::cout<<v1<<std::endl;
}
}

建议都加std::

string

1
std::string a(n,c)  //可以生成一个包含n个c的字符串。
1
2
3
4
5
6
7
8
9
10
std::string a.find() //可以找一个你想要东西的位置

std::string abbrevName(std::string name)
{
std::string s = "";
s += toupper(name[0]);
s += '.';
s += toupper(name[name.find(' ')+1]);
return s;
}

new和delete

释放动态的指针,如果释放一个值,就是delete p;如果释放的是一个数组,就是delete []p;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

int main() {
// 用 new 分配一个单个整数
int* p = new int(42);
cout << "单个int: " << *p << endl;
delete p; // 用 delete 释放

// 用 new[] 分配一个整数数组
int* arr = new int[5]; // 相当于 int arr[5];
for (int i = 0; i < 5; ++i) {
arr[i] = i * 10;
}
cout << "数组内容: ";
for (int i = 0; i < 5; ++i) {
cout << arr[i] << " ";
}
cout << endl;
delete[] arr; // 用 delete[] 释放数组

return 0;
}

STL——set

set 有insert,erase,count,find,size等用法,通常用于查找的时候不用find(因为要返回迭代器的值),而是用count,set底层是红黑树,可以实现自动除重,比如insert(6)两次,只会有一个,所以count()的值只会是0和1;用于判断是否存储在里面。

1
2
3
set.insert();
set.erase();
set.count();

unordered_set和set大体上一样,但是是用哈希表实现的,所以里面是无序的,但是查找很快,O(1)级,set的查找是O(logN);

set插入vector的元素很方便,使用迭代器,同时通过assign分配新值给nums替换旧值。

1
2
3
4
5
6
7
8
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
set <int> s (nums.begin(),nums.end());
nums.assign(s.begin(),s.end());
return nums.size();
}
};

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
bool containsDuplicate(vector<int>& nums) {
int n = nums.size();
set <int> s;
for (auto a:nums){
s.insert(a);
}
int ns = s.size();
if (n!=ns) return true;
return false;
}
};

判断前后两次的size是否相同,不同则有重复插入。

map

map是一个映射,由Key->Value,内部是用pair实现。
常见的用法有

  • m[] = ;
  • m.erase();
  • m.count(); //用于查找有没有值,return1/0;

    可以看到map也像set一样会自动排序,且后来者会覆盖,count返回的是0和1。

冒泡排序

外层循环a.size()-1次,内层循环a.size()-i-1次;加引用和不加引用的区别在于:通过引用传递,函数内部对向量的修改会直接反映在原始向量上,这正是冒泡排序需要的效果,即直接在原数组上排序。

1
2
3
4
5
6
7
8
9
10
11
12
void bubble(vector<int> &a){
for (int i = 0;i<a.size()-1;i++){
for (int j = 0;j<a.size()-1-i;j++){

if (a[j]>a[j+1]){
int temp = a[j+1];
a[j+1] = a[j];
a[j] = temp;
}
}
}
}

指针

指针是一种数据类型,在32为系统性size = 4,64 = 8.指针存放的是内存

1
2
int a = 5;
int *p = &a;

意思是p指针现在存储的是a的地址,可以使用解引用号来访问*p存储的这个地址的值

常量指针

const为常量,加在指针前就称之为常量指针 const int *p

1
2
3
4
int a = 10;
int b = 20;
const int*p = &a;
*p = 200;

这是一个非法的操作,常量指针可以改变指向的地址,但不能改变指向地址的值

1
2
3
4
int a = 10;
int b = 20;
const int *p = &a;
p = &b;

这是一个合法的操作,现在p指向的就是b的地址。

指针常量

1
2
3
int a = 0;
int b = 0;
int * const p = &a;

指针常量和常量指针作用刚好相反,要这样理解:const后面跟着谁谁就无法改变,在指针常量中,int *const p,const后面跟着的是地址,所以地址就不能改;在常量指针中,const int *p,const后面跟的是int值,所以值不可以改。
所以当const int *const p两个都不可以改。快速记忆:遇到英文翻译中文,const int *p,const是常量,后面是指针,所以常量指针;int *const p,先遇到指针,再遇到常量,所以叫指针常量。

引用

引用就是取别名,本质是指针,让b的地址和a的地址相同,但记住在这里引用类似于常量指针,不允许再更改成其他的地址了,但可以改值。引用必须初始化

1
2
int a = 10;
int &b = a;

引用不要返回局部变量

1
2
3
4
5
6
7
8
9
10
11
int &test()
{
int a = 10;
return a;
}

int main()
{
int &ref = test();
cout<<ref;
}

第一次的时候可以正常输出10,但第二次就不行了,因为局部变量存放在栈区,会被释放。

1
2
3
4
5
6
7
8
9
10
11
int &test()
{
static int a = 10;
return a;
}

int main()
{
int &ref = test();
cout<<ref;
}

static关键字让他是静态变量,存放在全局区,全局区上的数据在程序结束后释放。

类和对象

类有属性行为,比如一个学生类,属性就是学生的名字/学生的学号,行为就是打印出名字和学号,也可以用行为来给学生的属性赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class student{
public:
int m_num;
string m_name;
void Print()
{
cout<<m_num<<" "<<m_name;
}
void GetNum(int num)
{
m_num = num;
}
void GetName(string name)
{
m_name = name;
}
};

成员私有化之后可以实现只读/只写/可读可写。

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
class student{
private:
int m_age = 18;
string m_name;
string m_lover;
public:
void get_age()
{
cout<<m_age<<endl;
}
void set_age(int age)
{
if (age>150||age<0){
return;
}
m_age = age;
}
};

int main()
{
student s;
s.set_age(160);
s.get_age();
}

圆类

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
using namespace std;

class point{
public:
void setm_x(int x)
{
m_x = x;
}
int getm_x()
{
return m_x;
}
void setm_y(int y)
{
m_y = y;
}
int getm_y()
{
return m_y;
}

private:
int m_x;
int m_y;
};

class circle{
public:
void setm_r(int r)
{
m_r = r;
}
int getm_r()
{
return m_r;
}
void setm_center(point p)
{
m_center = p;
}
point getm_center()
{
return m_center;
}

private:
int m_r;
point m_center;
};

void Relation(circle &c,point &p)
{
int dx = c.getm_center().getm_x()-p.getm_x();
int dy = c.getm_center().getm_y()-p.getm_y();
cout<<dx<<" "<<dy;
int rdistance = c.getm_r()*c.getm_r();
int distance = dx*dx+dy*dy;

if (distance==rdistance){
cout<<"在圆上";
}
else if(distance>rdistance){
cout<<"在圆外";
}
else cout<<"在圆内";
}


int main()
{
point p;
circle c;
c.setm_r(10);
point center;
center.setm_x(10);
center.setm_y(0);
c.setm_center(center);

p.setm_x(10);
p.setm_y(10);

Relation(c,p);
}

构造函数与析构函数

没有返回值,不用写void,函数名与类名相同,构造函数可以有参数,可以发生重载,创建对象的时候会自动调用,而且只调用一次。
析构函数前加一个~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class person
{
public:
person()
{
cout<<"构造";
}
~person()
{
cout<<"析构";
}
};

int main()
{
person p;
person p1;
}

继承

可以省略冗余的代码,继承父类。
继承语法: class 子类 : 继承方式 父类
class cpp : public header
;
;
子类可以缩小权限范围,但不能扩大权限范围。

类初始化

类初始化在构造函数后面打一个冒号

1
2
3
4
5
6
class circle{
public:
Point(int xx,int yy):x(xx),y(yy);
private:
float x,y;
};

上面这一行代码等价于下面这一行代码

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point
{
public:
Point(int xx, int yy)
{
x = xx;
y = yy;
cout << "Constructor of Point" << endl;
}

private:
float x, y;
};

但是区别不同
上面两段代码对应于初始化类成员的两种方式:(1)使用初始化列表;(2)在构造函数体内进行赋值操作。
但严格来说,上面两段代码只是能实现相同的功能(初始化Point类的对象),它们的本质并不相同,下面来说明原因。
构造函数的执行分为两个阶段:
(1)执行初始化列表:初始化类中的数据成员;
(2)执行构造函数体:一般是对类中的数据成员进行赋值操作。
初始化与赋值是不同的,所以上面两段代码只是功能上相同,但本质并不相同,前一个是初始化,后一个是赋值。

STL–deque

deque(double ended queue)双端队列 好处:两端都开口,想要在头部插入元素很方便。

有四种拷贝构造

  • dequedeqT 默认构造
  • dequed1(d); 拷贝构造
  • dequed2(d.begin(),d.end()) 把[begin,end)区间的元素给d2
  • dequed3(10,100) //10个100 将n个elem拷贝给自身

有三种赋值

  • deque d = d1 等号赋值
  • deque d3; d3.assign(d1.begin(),d1.end())
  • d3.assign(10,100) 给10个100
    和vector一样。

大小

函数原型:
deque.empty(); //判断容器是否为空
deque.size(); //返回容器中元素的个数
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

插入和删除

两端插入操作:
push_back(elem); //在容器尾部添加一个数据
push_front(elem); //在容器头部插入一个数据
pop_back(); //删除容器最后一个数据
pop_front(); //删除容器第一个数据
指定位置操作:
insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
clear();//清空容器的所有数据
erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos); //删除pos位置的数据,返回下一个数据的位置。

排序

sort(d.begin(),d.end())

STL–queue

先进先出,很像尾插法。

不允许遍历!只能访问队头队尾!
入队:q.push() 出队:q.pop()
基本操作

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
#include <iostream>
using namespace std;

bool compare(int a, int b)
{
cout << "compare_int_int" << endl;
return a > b;
}

bool compare(double a, double b)
{
cout << "compare_double_double" << endl;
return a > b;
}

/*
* //strcmp会逐个比较
const char* a = "apple";
const char* b = "apricot";
strcmp(a, b); // 比较 'a' vs 'a' → 'p' vs 'p' → 'p' vs 'r' → 返回负数('p' < 'r')
*/
const char* a = "hello"; //在内存中是'h','e','l','l','o','\0',a是一个指针,指向'h'的地址。
bool compare(const char *a, const char *b) //const char* a 是一个指针,指向一个字符串的起始地址,字符串本质上是以 \0 结尾的 char 数组。
{
cout << "compare_char*_char*" << endl;
return strcmp(a,b)>0;
}

int main()
{
compare(10, 20);
compare(10.0, 20.0);
compare("aaa", "bbb");
}