关键字的作用
int main() {
int initNum = 3;
for (int i=1; i<=5; i++) {
static int n1 = initNum;
n1++;
printf("%d\n", n1);
}
return 0;
}
/*
4
5
6
7
8
*/
int main() {
int initNum = 3;
for (int i=1; i<=5; i++) {
static int n1 = initNum;
{
int *p = &n1;
p++;
*p = 0;
}
n1++;
printf("%d\n", n1);
}
return 0;
}
/*
4
4
4
4
4
*/
在编译过程中,每一个函数都有一个入口地址,而函数指针就是指向该入口地址的指针。
#include<iostream>
using namespace std;
void fun1(int x) {
cout << x << endl;
}
void fun2(int x) {
cout << x+x <<endl;
}
int main() {
void (*pf)(int);
pf = fun1;
pf(222);
pf = fun2;
pf(222);
}
#include<iostream>
using namespace std;
class Shape {
public:
void show() { // 未定义为虚函数
cout << "Shape::show()" << endl;
}
void virtual show() { // 定义为虚函数
cout << "Shape::show()" << endl;
}
};
class Line : public Shape {
public:
void show() {
cout << "Line::show()" << endl;
}
};
class Point : public Shape {
public:
void show() {
cout << "Point::show()" << endl;
}
};
int main() {
Shape *pt;
pt = new Line();
pt->show();
pt = new Point();
pt->show();
return 0;
}
/*
未定义为虚函数时输出结果
Shape::show()
Shape::show()
定义为虚函数时输出结果
Line::show()
Point::show()
*/
有些情况下,基类生成的对象是不合理的,比如动物可以派生出狮子、孔雀等,这些派生类显然存在着较大的差异。那么可以让基类定义一个函数,并不给出具体的操作内容,让派生类在继承的时候在给出具体的操作,这样的函数被称为纯虚函数。含有纯虚函数的类成为抽象类,抽象类不能声明对象,只能用于其他类的继承。 纯虚函数的定义方法为:
void ReturnType Function() = 0;
子类可以不重写虚函数,但一定要重写纯虚函数。
静态函数在编译时就确定了调用它的时机,而虚函数在运行时动态绑定,虚函数由于用到了虚函数表和虚函数虚函数指针,会增加内存使用。
符号,同样无返回类型。若对象在调用过程中用
动态分配了内存,可以在析构函数中写
语句统一释放内存。
++默认的析构函数不是虚函数,只要当其作为父类的时候,才会设置为虚函数。
可以通过 attribute 关键字,声明 constructor 和 destructor 来实现。
#include<iostream>
using namespace std;
__attribute((constructor)) void before_main() {
cout << __FUNCTION__ << endl;
}
__attribute((destructor)) void after_main() {
cout << __FUNCTION__ << endl;
}
int main() {
cout << __FUNCTION__ << endl;
return 0;
}
在有虚函数的类中,存在一个虚函数指针,该指针指向一张虚函数表,当子类继承基类的时候,也会继承其虚函数表。当子类重写基类中的虚函数时,会将虚函数表中的地址替换成重写的函数地址。
char *s1 = "abc";
char s2[] = "abc"
int x = 10;
const int *a = &x;
int* const b = &x;
为了支持 不定长参数函数
int add(int num, ...) {
va_list valist;
int sum = 0;
int i;
va_start(valist, num);
for (i = 0; i < num; i++) {
sum += va_arg(valist, int);
}
va_end(valist);
return sum;
}
enum color{red, green, blue, yellow};
enum color2{red, green}; // ERROR,因为 red 和 green 已经在 color 中定义过了
auto x = red; //OK,因为 red 没有限定作用域
auto y = color::red; //OK
enum struct color{red, green, blue, yellow};
enum struct color2{red, green}; // OK,red 和 green 在不同作用域内
auto x = red; // ERROR,red 没有指定作用域
auto y = color::red;
强类型转换的优点在于
#include<iostream>
using namespace std;
class Node {
public :
string s1;
int a;
Node(string s, int val=0) : s1(s), a(val) {
}
bool ok(Node other) const {
return s1 == other.s1;
}
};
int main() {
Node x("xxxxx");
cout << x.ok(string("xxxxx")) << endl; // 隐式
cout << x.ok(Node("xxxxx")) << endl; // 显式
return 0;
}
每一个函数调用都分配一个函数栈,先将返回地址入栈,在将当前函数的栈指针入栈,然后在栈内执行函数。
#include<iostream>
using namespace std;
int x, y;
int get1() {
cout << "get1 中 x 的地址" << &x << endl;
return x;
}
int& get2() {
cout << "get2 中 y 的地址" << &y << endl;
return y;
}
int main() {
int x = get1();
cout << "main 中 x 的地址" << &x << endl;
int& y = get2();
cout << "main 中 y 的地址" << &y << endl;
return 0;
}
/*
get1 中 x 的地址0x4c600c
main 中 x 的地址0x6efef8
get2 中 y 的地址0x4c6010
main 中 y 的地址0x4c6010
*/
不能,会造成无限循环。
#include<iostream>
using namespace std;
class Node {
public:
int x;
Node(int a) : x(a) {
}
Node(Node& a){ // 方法1,正确
x = a.x;
}
Node(Node a) { // 方法2,错误,无法编译通过
x = a.x;
}
};
int main() {
Node x(10);
Node y(x);
return 0;
}
int *p = (int*)malloc(sizeof(int)*100);
free(p);
int *a = new int;
delete a;
int *q = new int[100];
delete[] q;
是标准函数库 | 是 ++ 运算符 |
从堆分配内存 | 从自由存储区分配内存 |
需要显式指出分配内存大小 | 编译器自行计算 |
不会调用构造/析构函数 | 会调用构造/析构函数 |
返回无类型指针 () | 返回有类型指针 |
不可调用 | 可以基于 |
不可被重载 | 可以被重载 |
构造函数分为初始化和计算两个阶段,第一阶段对应初始化列表,第二阶段对应函数主体,引用必须在第一阶段完成。
#include <iostream>
using namespace std;
class Node {
public:
int& a;
int b, c, d;
Node(int &x, int y, int z, int k) : a(x), b(y), c(z), d(k) {
}
};
int main() {
int t = 1;
Node x(t, 2, 3, 4), y(t, 2, 3, 4);
x.a++;
cout << y.a << endl;
return 0;
}
const int x = 10;
const int &y = x;
const int &z = 20;
#include<bits/stdc++.h>
using namespace std;
void func(int& x) {
cout << "左值引用" << endl;
}
void func(int&& x) {
cout << "右值引用" << endl;
}
void func(const int& x) {
cout << "const 左值引用" << endl;
}
void func(const int&& x) {
cout << "const 右值引用" << endl;
}
template<typename T> void fun(T&& x) {
func(forward<T>(x));
}
int main() {
fun(10);
int x = 0;
fun(x);
fun(move(x));
const int y = 0;
fun(y);
fun(move(y));
return 0;
}
/*
右值引用
左值引用
右值引用
const 左值引用
const 右值引用
*/
通过红黑树实现 | 通过 表实现 |
操作复杂度 级别 | 操作复杂度常数级别 |
内部有序 | 内部无序 |
适用于对顺序有要求的场景 | 适用于频繁查找的场景 |
vector | list | |
---|---|---|
类型 | 动态数组 | 动态链表 |
底层实现 | 数组实现 | 双向链表实现 |
访问 | 支持随机访问, | 不支持随机访问, |
插入 | 在末尾 ,在中间 | 很快, |
删除 | 在末尾 ,在中间 | 很快, |
内存来源 | 从堆区分配空间 | 从堆区分配空间 |
内存使用 | 是顺序内存 | 不是顺序内存 |
内存分配 | 一次性分配好,不够时扩容 | 每次插入节点都需要进行内存申请 |
性能 | 访问性能好,插入删除性能差 | 插入删除性能好,访问性能差 |
适用场景 | 经常随机访问,不在乎插入和删除效率 | 经常插入删除,不在乎访问效率 |
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> g;
for(int i=1; i<=10; i++)
g.push_back(i);
cout << "capacity = " << g.capacity() << endl;
vector<int>().swap(g);
cout << "capacity = " << g.capacity() << endl;
return 0;
}
#include <bits/stdc++.h>
using namespace std;
int main() {
{
set<int> st = {1, 2, 3, 4, 5, 6};
for(set<int>::iterator iter=st.begin(); iter!=st.end(); ) {
if(*iter == 3) {
st.erase(iter++); // 传给 erase 的是 iter 的一个副本
} else {
iter++;
}
}
}
{
vector<int> g = {1, 2, 3, 4, 5, 6};
for(vector<int>::iterator iter=g.begin(); iter!=g.end(); ) {
if(*iter == 3) {
iter = g.erase(iter);
} else {
iter++;
}
}
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
void print() {
cout << endl;
}
template<class T, class... Args>
void print(T num, Args... rest) {
cout << num << " ";
print(rest...);
}
int main() {
print(1, 2, 3, 4, 5);
return 0;
}
// head.h 内容
struct Node {
int a, b;
};
// main.h 内容
Node node
/// main.cpp 错误写法
#include "main.h"
#include "head.h"
int main() {
return 0;
}
// main.cpp 正确写法
#include "head.h"
#include "main.h"
int main() {
return 0;
}
使用尖括号和双引号的区别在于:编译器预处理阶段寻找头文件的路径顺序不一样。
程序申请内存的时候,超出了系统实际分配给你的空间,此时系统无法完成满足你的需求,就会发生内存溢出。
内存溢出的情况:
#include<bits/stdc++.h>
using namespace std;
class A;
class B;
class A {
public:
A() {
cout << "A Created" << endl;
}
~A() {
cout << "A Destroyed" << endl;
}
shared_ptr<B> ptr;
};
class B {
public:
B() {
cout << "B Created" << endl;
}
~B() {
cout << "B Destroyed" << endl;
}
shared_ptr<A> ptr;
};
int main() {
shared_ptr<A> pt1(new A());
shared_ptr<B> pt2(new B());
pt1->ptr = pt2;
pt2->ptr = pt1;
cout << "use of pt1: " << pt1.use_count() << endl;
cout << "use of pt2: " << pt2.use_count() << endl;
return 0;
}
/*
A Created
B Created
use of pt1: 2
use of pt2: 2
*/
#include<bits/stdc++.h>
using namespace std;
class A;
class B;
class A {
public:
A() {
cout << "A Created" << endl;
}
~A() {
cout << "A Destroyed" << endl;
}
weak_ptr<B> ptr;
};
class B {
public:
B() {
cout << "B Created" << endl;
}
~B() {
cout << "B Destroyed" << endl;
}
weak_ptr<A> ptr;
};
int main() {
shared_ptr<A> pt1(new A());
shared_ptr<B> pt2(new B());
pt1->ptr = pt2;
pt2->ptr = pt1;
cout << "use of pt1: " << pt1.use_count() << endl;
cout << "use of pt2: " << pt2.use_count() << endl;
return 0;
}
/*
A Created
B Created
use of pt1: 1
use of pt2: 1
B Destroyed
A Destroye
*/
因为存在这种情况:申请的空间在函数结束后忘记释放,造成内存泄漏。使用智能指针可以很大程度的避免这个问题,因为智能指针是一个类,超出类的作用范围后,类会调用析构函数释放资源,所以智能指针的作用原理就是在函数结束后自动释放内存空间。