抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

C++概述

由C发展而来,兼容C

组成:

  • C语言
  • 面向对象设计
  • 泛型编程
  • 标准模板库(STL)

数据类型

整型、浮点型、字符型、布尔型、空类型(void)、指针(*)、引用(&)

结构体(struct)、共用体(union)、枚举(enum)、类(class)

整型字节长度:

  • 一般short int(2B) <= int(4B) <= long int (4B、8B)

类型转换:

  • 隐式类型转换:存储空间提升原则(short -> int-> long, signed->unsigned…)
  • 显式类型转换,用法:自右向左(type)expr、自左向右type(expr)

变量

可以指定变量的存储空间

1
2
3
4
5
6
7
8
auto int a;  
//自动存储,默认使用(auto可以不写)

register int a;
//使用cpu的寄存器来存放局部变量,变量不能用寄存器存储时,会转为auto存储

static int a;
//静态存储,声明周期与程序运行周期相同,函数未调用时已经分配好空间,但作用域为块作用域

函数

  • 无返回值函数:返回语句用 return; 或不使用返回语句
  • 有返回值函数:返回语句只能用 return 表达式;

main函数由操作系统调用,返回0表示正常退出,否则表示异常退出

形式参数:函数在未调用前不占用内存

参数入栈:最右边的参数先进栈

const参数:用于限制对一个对象的修改操作,函数体内不能对该对象修改。

对于基本类型,不必使用const限定参数,但对于数组、指针这种传进来的是地址的参数,就有必要了

可变参数,用法:

1
2
3
返回类型 func(type a,...){}  //用三个点来表示可变参数,三个点只能位于最后,前面至少有一个普通的参数

//获取可变参数方式:需要 #include<cstdarg>,具体可以百度

函数原型(或函数声明),如:(注意区别于函数定义)

1
int add(int a, int b);  //参数名可以省略

默认参数:只能设置一次(函数声明中设置了默认参数,函数定义就不能再设置,反之也一样)

  • 一般在声明时设置函数参数

  • 默认参数要自右向左设置

  • 默认值可以是常数或全局变量,甚至是一个函数的调用(但调用的函数的实参必须是常量或全局变量,如下)

    1
    void func1(int a, int b=func2(666)); //调用的函数func2传入的参数是常数666

内联函数

编译时直接将函数代码嵌入主调函数中,取消调用环节

内联函数的函数原型

1
inline 返回类型 funcName(形参列表);

内联函数定义

1
2
3
inline 返回类型 funcName(形参列表){

}

注意

  1. 内联函数不能是递归函数
  2. 内联函数内部不能用 【循环语句】和【switch】语句
  3. 编译器无法将内联函数嵌入时,会忽略 inline声明,按照普通函数处理

函数重载

针对的是一组同名函数

特征:

  • 函数名相同,参数列表(参数类型或个数)不同
  • 返回类型不一定相同
  • 函数体没有要求

调用时,编译器根据传入的实参判断该使用哪个函数

函数模板

目的:设计通用型函数,与类型无关

定义:

1
2
3
4
5
template<typename T1, typename T2> 返回类型 funcName(参数列表){

}
// <typename T1, typename T2>表示模板参数列表
// typename 也可以换成class,但不建议换

如:

1
2
3
template<typename T> T add(T a, T b){
return a+b;
}

模板参数列表不必都表示类型,也可以是非类型形参(调用时要传入值),如

1
2
3
4
5
6
template<typename T1, int N> void func(T a){
cout<<N<<" "<<a;
}

//调用:
func<int, 100>(20);

内联模板函数

1
2
3
template<typename T1, typename T2> inline 返回类型 funcName(参数列表){

}

内存结构

  • 代码段(text)

    存放程序执行的机器指令

  • 已初始化数据段(data)

    全局变量、静态变量,包括字符串、数组等常量

    基本类型的常量一般被编译成指令存放在【代码区】

  • 未初始化数据段(bss)

    程序没有运行的时候bss段是不存在的,在运行时(main函数运行前)由操作系统根据变量类型分配内存空间

    main函数运行前会把变量初始化为0,保证【全局、静态】变量在main函数运行前就已经分配好内存空间。

  • 存放所有局部、非静态变量;临时变量,包含函数形参、返回值

    函数调用时,才分配存储空间,调用结束,释放相应内存空间

  • 存放动态分配的存储空间,其存储空间由程序员分配和释放,即执行到相应的代码块才会分配或释放存储空间

文件包含

1
2
3
4
5
6
#include <xxx>  //只搜索系统路径的头文件
#include "xxx" //先搜索源文件所处文件夹,再搜索系统路径的头文件

// 头文件路径不是C++语法,因此不能理解为字符串,例如
#include "D:\a\b\c.h" //正确
#include "D:\\a\\b\\c.h" //错误

使用 #ifdef、#ifndef等避免重复包含头文件(或 #pragma once 特殊预处理命令)

引用类型

主意啊,这里的"引用"要理解为名词,而不是动词

简单说来就是变量的别名,声明方式:

1
类型 &引用名=被引用的对象名

如:

1
2
int x;
int &r=x;

引用的本质

本质是位于某个内存地址上的指定类型的对象。

上例中 rx占用内存中【同一个存储单元】

其它需要注意的点:

  • 引用在声明时必须指定引用的对象是谁

  • 引用在声明后,不能再修改作为其它对象的引用

    • 注意:函数参数是引用时,通常用const修饰,表示引用的对象不能通过引用修改,而不是说引用不能被修改(因为引用本身就不能被修改)
  • 编译器不会专门开辟内存单元存储引用

  • 引用的类型被引用的类型要相同

函数返回类型是引用?

首先说明:函数返回时会产生一个【临时变量】作为函数返回值的副本(因为函数调用后局部变量会被销毁),而返回引用时不会产生临时变量。因此

  • 千万不要引用函数中的局部变量(因为如果函数销毁了,引用变量指向谁?)如
  • 如果函数参数是引用,可以返回引用,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int& func(int &a, int&b){
return a;
}

int main()
{
int x=1, y=2, z=9;
func(x, y) = z; //因为函数内return a; a引用的是x,所以这相当于x=z;
cout << "x=" << x <<endl;
cout << "y=" << y <<endl;
}

//运行结果:
//x=9
//y=2

常引用

如果是常引用,表示不能通过引用来修改对象的值

1
2
3
4
int a=66;
const int &r = a;
a = 55; //正确
r = 10; //错误,不能通过引用来修改对象的值

C++对象创建2种方式

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
#include<stdio.h>
using namespace std;
class ObjectClass{
public:
void fun(){
printf("Hello, Im c++ objectClass\n");
}
};

int main(){
ObjectClass obj1;//栈中分配 ,由操作系统进行内存的分配和管理
ObjectClass obj2 = obj1; //栈中分配 ,由操作系统进行内存的分配和管理
obj1.fun();//"." 是结构体成员引用
obj2.fun();//"." 是结构体成员引用

ObjectClass *obj3 = new ObjectClass(); //【堆】中分配 ,由管理者进行内存的分配和管理,用完必须delete(),否则可能造成内存泄漏
obj3->fun();//->是指针引用
delete(obj3);
return 0;
}

//运行
root@A920:/system/bin # ./ObjectClass
Hello, Im c++ objectClass
Hello, Im c++ objectClass
Hello, Im c++ objectClass

上面就是具体的演示代码,下面分析一下几种创建方式:

(1)第一种和第二种:这两种没什么区别,一个隐式调用,一个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存。用这种方法创建的对象,内存分配到栈里(Stack)。使用 “.” 而不是 “->” 调用对象的方法。当程序离开对象的使用范围(如方法结束,一个程度块的最后{}),范围内的栈中的对象会自动删除,内存自动回收。这是创建对象最简单的方式栈是系统数据结构,对于线程/进程是唯一的,它的分配和释放由操作系统决定,不需要由开发者来管理。在执行函数时,函数内局部变量的存储单元可以在栈上创建,函数执行完毕,系统会自动释放这些存储单元。

(2)第三种:使用了new,在堆中分配了内存,堆上的内存分配,亦称动态内存分配。程序在运行的期间用malloc申请的内存,这部分内存由程序员自己负责管理,其生存期由开发者决定:在何时分配,分配多少,并在何时用free来释放该内存。这是唯一可以由开发者参与管理的内存。使用的好坏直接决定系统的性能和稳定。

注意:栈中内存的分配和管理由操作系统决定,而堆中内存的分配和管理由管理者决定。在堆中的对象不会自动删除,内存不会自动回收,所以new一个对象使用完毕,必须调用delete,释放内存空间。也就是说,new和delete必须成对出现

C++大、小根堆

默认创建大根堆(会自动对元素排序,然后从大到小插入素),遵循FIFO原则(即大的先进,也先出)

创建小根堆要加入 greater 参数

1
2
3
4
5
6
priority_queue<int> s;
priority_queue<int, vector<int>, less<int>> s;
//以上两句代码等价。less表示按照递减(从大到小)的顺序插入元素

priority_queue<int, vector<int>, greater<int>> s;
//greater表示创建小根堆,即按照递增(从小到大)的顺序插入元素

常用操作

1
2
3
4
5
s.push(1); //插入队尾
s.pop(); //弹出队头
top(); //查询顶部元素
empty();
size();

评论