C++ 完整的 CHM 版离线手册,可以 从这里下载。
C语言中的标准库头文件,例如 math.h 和 stdio.h,在C++中被命名为 cmath 和 cstdio:
#include <cmath> #include <cstdio> int main() { double a = 1.2; a = sin(a); printf("%lf\n", a); }为了防止重名,通过 :: 运算符决定某个名字属于哪个命名空间。不加限定时,表示使用全局命名空间。
标准库中的名字,属于标准命名空间 std,例如 using std::cout 表示使用标准库中的标准输出流。
通常有三种方法使用命名空间中的某个名字:
using namespace XX; // 引入整个命名空间 using XX::name; // 引入单个名字 XX::name; // 程序中直接通过命名空间使用其中的名字 #include <cstdio> namespace first { int a; void f(){} int g(){return 666;} } namespace second { double a; double f(){return 3.14;} char g; } int main() { first::a = 2; second::a = 3.14; first::a = first::g() + second::f(); second::a = first::g() + 1.23; printf("%d\n", first::a); printf("%lf\n", second::a); return 0; }C++ 中,输入输出都是流,通过输入输出流库(头文件 iostream),使用输出运算符 << 和 输入运算符 >> 进行输入输出。
cout :标准输出流对象(屏幕)cin:标准输入流对象(键盘) #include <iostream> using namespace std; int main() { double a; cout << "输入一个浮点数,回车结束" << std::endl; // endl表示换行符,同时强制输出 std::cin >> a; // 从标准输入流获取输入,并存入 a cout << a * a; return 0; }C语言中的变量都是值类型,每个变量对应一块内存。C++中引入了“引用类型”,引用类型的变量相当于一个具体的变量的“别名”。
使用引用可以减少内存的拷贝,当对象较大时可以提高效率。为了防止使用引用后,无意中修改了原对象,可以通过 const int &x 的形式来限定:
#include <iostream> using namespace std; void change(int &x, const int &y, int z) { x = 666; y = 667; // error: assignment of read-only reference 'y' z = 668; } int main() { int a = 1, b = 2, c; change(a, b, c); cout << a << " " << b << " " << c << endl; return 0; }用 inline 关键字声明的函数称为内联函数。编译器碰到内联函数时,会用函数的代码替换这个函数,称为“内敛展开”,从而避免了函数调用的开销,提高程序执行效率。
#include <iostream> #include <cmath> using namespace std; inline double area(double x, double y) { return sqrt(x * x + y * y); } int main() { double a = 3, b = 4; cout << area(a, b) << endl; return 0; }C++支持异常处理。异常事件发生时用 throw 关键字抛出异常表达式,抛出点称为异常出现点,由操作系统为程序设置当前异常对象,然后执行异常处理代码,顺序如下:
执行当前异常处理代码块,依次匹配catch语句中的异常对象(只进行类型匹配)。若匹配成功,则执行catch块内的异常处理语句,然后接着执行try…catch…块之后的代码。如果在当前的try…catch…块内找不到匹配该异常对象的catch语句,则由更外层的try…catch…块来处理该异常。如果当前函数内所有的try…catch…块都不能匹配该异常,则递归回退到调用栈的上一层去处理该异常。如果一直到主函数main()都不能处理该异常,则调用系统函数terminate()终止程序。 #include <iostream> using namespace std; int main() { int score = 0; try { cin >> score; if (socre > 100 || score < 0) throw score; cout << "分数保存成功“; } catch (int score) { cerr << "请输入 0 - 100 之间的分数" << endl; } }C++ 中,函数的参数可以带默认值,带默认值的参数必须放在最右边:
void test(int a, int b = 10, int c = 20) {...}C++ 中允许函数重名,只要形参不一样(个数或类型不一样)。调用函数时会根据实参和形参自动匹配最佳的函数。
#include <iostream> using namespace std; void test(int a, int b) { cout << a + b << endl; } void test(int a, double b) { cout << a + b << endl; } int main() { test(3, 3.14); test(3, 5); return 0; }重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
#include <iostream> using namespace std; struct Vector2{ double x; double y; }; Vector2 operator * (double a, Vector2 b) { Vector2 r; r.x = a * b.x; r.y = a * b.y; return r; } int main() { Vector2 b, k; b.x = 3.14; b.y = 6.66; k = 3.14 * b; cout << k.x << endl; }C++ 是强类型的语言,函数或类需要针对每一种具体类型进行操作时,代码大量冗余。通过模板类或模板函数,可以编写不针对某一具体类型的代码,编译器在编译时,可以针对具体数据类型,自动用模板函数生成针对该具体数据类型的函数或类。
可以参考 C++ Template 进阶指南。
可以参考 C++ Template 基础篇(一):函数模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。 模板是创建泛型类或函数的蓝图。库容器比如迭代器和算法,都是泛型编程的例子,使用了模板的概念。 每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector 或 vector 。
模板函数定义如下,其中 type 是函数所使用的数据类型的占位符名称。这个名称可以在函数定义中使用:
template <class type> ret-type func-name(parameter list) { // 函数主体 }求不同数据类型最小值的模板函数:
#include <iostream> using namespace std; template <class T> T minVal(T a, T b) { return a > b ? b : a; } int main() { int a = 3, b = 4; cout << "min of int is: " << minVal(a, b) << endl; double x = 3.14, y = 666; cout << "min of double is: " << minVal(x, y) << endl; }上面例子中,当两个数的类型也不同时,编译时会报错。要增加对不同类型数据的比较功能,需要改写一下:
#include <iostream> using namespace std; template <class T, class U> T minVal(T a, U b) { return a > b ? b : a; } int main() { int a = 3, b = 4; cout << "min of int is: " << minVal(a, b) << endl; double x = 3.14, y = 666; cout << "min of double is: " << minVal(x, y) << endl; cout << "min of mix is: " << minVal(a, y) << endl; }C语言中通过 malloc/alloc/realloc 和 free 分配和释放内存(在堆存储区)。C++ 则通过 new 和delete 进行,会自动调用构造函数和析构函数。
#include <iostream> #include <cstring> using namespace std; int main() { double d = 3.14; double *p = &d; cout << *p << " " << p << endl; p = new double; *p = 3.1415926; cout << *p << " " << p << endl; delete p; p = new double[4]; p[0] = 6.66; p[1] = 7.77; cout << p[0] << " " << *(p+1) << endl; delete[] p; char *s = new char[100]; strcpy(s, "hello world"); cout << s << endl; }在类中可以使用 this 指针来表示类本身。
#include <iostream> using namespace std; struct Date { int y, m, d; void print() { cout << y << "-" << m << "-" << d << endl; } void init(int yy, int mm, int dd) { y = yy; m = mm; d = dd; } Date& add(int a) { d += a; return *this; } }; int main () { Date date; date.init(2000, 8, 15); date.print(); date.add(3).add(5); date.print(); return 0; }C++ 的类中,与类名同名,且没有返回值的函数就是构造函数。函数初始化时会首先自动调用构造函数。
不需要任何参数的构造函数称为默认构造函数。如果没有定义任何构造函数,则编译器会自动生成默认构造函数。只要定义了自己的构造函数,则不再存在默认构造函数,如果需要,必须自己定义。
#include <iostream> using namespace std; struct Date { int y, m, d; void print() { cout << y << "-" << m << "-" << d << endl; } Date() { y = 2018; m = 1; d = 1; } Date(int yy, int mm, int dd) { y = yy; m = mm; d = dd; } Date& add(int a) { d += a; return *this; } }; int main () { Date date1; date1.print(); Date date(2000, 8, 15); date.print(); date.add(3).add(5); date.print(); return 0; }构造函数中,可以使用默认值:
#include <iostream> #include <cstring> using namespace std; struct Student { int age; char * name; Student(char * n = "no_name", int a = 18) { // *name = *n; int len = strlen(n); name = new char[len + 1]; strcpy(name, n); age = a; } }; int main () { Student s1; cout << s1.name << "\t" << s1.age << endl; Student s2("lisi"); cout << s2.name << "\t" << s2.age << endl; Student s3("zs", 33); cout << s3.name << "\t" << s3.age << endl; return 0; }C++ 中,类的同名函数前加 virtual ~ 表示是析构函数。析构函数在类销毁前自动调用,用于执行清理任务(例如关闭打开的文件、网络,释放内存等)。
#include <iostream> #include <cstring> using namespace std; struct Student { int age; char * name; Student(char * n = "no_name", int a = 18) { // *name = *n; int len = strlen(n); name = new char[len + 1]; strcpy(name, n); age = a; } virtual ~Student() { cout << name << " destroctur" << endl; delete[] name; } }; int main () { Student s1; cout << s1.name << "\t" << s1.age << endl; Student s2("lisi"); cout << s2.name << "\t" << s2.age << endl; Student s3("zs", 33); cout << s3.name << "\t" << s3.age << endl; return 0; }定义类对象时,可以用同类型的已有对象进行初始化,此时编译器会自动创建拷贝构造函数并调用。
#include <iostream> #include <cstring> using namespace std; struct Student { int age; char * name; Student(char * n = "no_name", int a = 18) { int len = strlen(n); name = new char[len + 1]; strcpy(name, n); age = a; } virtual ~Student() { cout << name << " destroctur" << endl; delete[] name; } }; int main () { Student s1; Student s2(s1); cout << s2.name << "\t" << s2.age << endl; return 0; }也可以自己实现拷贝构造函数:
Student(const Student &s) { name = new char[100]; strcpy(name, s.name); age = s.age; cout << "这是拷贝构造函数" << endl; }可以使用赋值运算符,将一个对象赋值给另一个对象。
#include <iostream> #include <cstring> using namespace std; struct Student { int age; char * name; Student(char * n = "no_name", int a = 18) { int len = strlen(n); name = new char[len + 1]; strcpy(name, n); age = a; } virtual ~Student() { cout << name << " destroctur" << endl; delete[] name; } }; int main () { Student s1; Student s2 = s1; cout << s2.name << "\t" << s2.age << endl; return 0; }也可以自己实现赋值运算符:
Stuednt& operator = (const Student &s) { name = new char[100]; strcpy(name, s.name); age = s.age; cout << “自己的赋值运算符" << endl; return *this; }输出:
这是拷贝构造函数 自己的赋值运算符 no_name 18 no_name destroctur no_name destrocturC++ 的可见性限定符有3中:
名称可见性public所有地方都可以访问private仅类自身可以访问protect仅类及其子类可以访问通常,属性不可以直接暴露在外部,而是通过接口来访问(查看或修改)。
C++要求使用前先声明。如果把成员定义放到类声明中,当类中的两个定义互相调用时,那么这两个声明无论谁先后,最先的那个总是使用到了未声明的符号;如果把成员定义和类声明分开,就可以在定义使用之前把所需的声明全列出来。
类的成员函数,包括构造函数,都可以在类的外部定义(方法名前需要加 类名::),但是需要在类的内部声明。
#include <iostream> #include <cstring> using namespace std; struct Student { private: int age; char * name; public: Student(char * n = "no_name", int a = 18) { int len = strlen(n); name = new char[len + 1]; strcpy(name, n); age = a; } virtual ~Student() { delete[] name; } char * getName(); }; char * Student::getName() { return name; } int main () { Student s1("xiaoming"); // cout << s1.name << "\t" << s1.age << endl; cout << s1.getName() << endl; return 0; }参考 这里
C++ 是强类型的语言。如果针对每种类型写一个类,冗余太高,所以有了类模板。
类模板是忽略具体类型的公共逻辑的抽象,通常用来作为容器(例如:vector)或者行为(例如:clonable)的封装。最简单的例子:
#include <iostream> using namespace std; template <class T> class myT { public: T max(T a, T b) { return a > b ? a : b; } }; int main() { myT<int> m1; cout << m1.max(1, 2) << endl; return 0; }与模板函数不同,类模板不能推断实例化。所以必须在实例化时显式指定类型参数,例如:Printer<int> p(1),而不能让编译器自行推断 Printer p(1)。
#include <iostream> using namespace std; template <class T> class mypair { T a, b; public: mypair (T first, T second) {a=first; b=second;} T getmax (); }; template <class T> T mypair<T>::getmax () { T retval; retval = a>b? a : b; return retval; } int main () { mypair <int> myobject (100, 75); cout << myobject.getmax(); return 0; }参考 C++ typedef用法小结。
typedef 用于为已有的类型创建别名。
typedef是定义了一种类型的新别名,#define 则是简单的字符串替换。
#include <iostream> #include <typeinfo> using namespace std; typedef char * PCHAR; // 一般大写 int main() { char * p1, p2; // p1 是字符指针类型,p2 是字符类型 cout << typeid(p1).name() << " " << typeid(p2).name() << endl; PCHAR p3, p4; // p3 p4 都是字符指针类型 cout << typeid(p3).name() << " " << typeid(p4).name() << endl; return 0; }C++ 支持 C 风格的字符串(用 null 字符 ‘\0’ 终止的一维字符数组),同时还引入了 string 类类型。
C 风格: 可以引入 cstring 这个 C 语言的字符串库,使用 strcpy、strlen、strcat、strcmp 等方法: #include <iostream> using namespace std; int main() { char str[] = "hello world"; char str2[3] = "AB"; // 结束字符占一个字符,所以这里最多放两个 cout << str << endl; cout << str2 << endl; cout << str[2] << endl; return 0; } String 类: #include <iostream> #include <string> using namespace std; int main() { string str = "hello world"; cout << str << endl; string str2("ABC"); cout << str2 << endl; string str3(str); cout << str3 << endl; string str4(str.begin(), str.end()-2); cout << str4 << endl; string str5(str, 5); cout << str5 << endl; cout << str.size() << endl; cout << str + str2 << endl; }输出:
hello world ABC hello world hello wor world 11 hello worldABCSTL:标准模板库(Standard Template Library),是 C++ 内置支持的库。STL 利用的 C++ 的类模板和函数模板机制,由容器、算法和迭代器组成。
容器::数据存储上,有一种对象类型,它可以持有其他对象或指向其他对象的指针,这种对象类型叫容器。
容器就是保存其他对象和相关方法的对象,是对特定代码重用问题的良好的解决方案。
STL 提供的容器有:
顺序容器:线性表结构 vector容器:从后面快速插入和删除,访问任一元素deque双端数组:从前后两侧快速插入和删除,访问任一元素list链表:从任何位置插入和删除关联容器:二叉树结构 set容器:快速查找,不允许重复值multiset容器:快速查找,允许重复值map容器:一对多映射,基于关键字快速查找,不允许重复值multimap容器:一对多映射,基于关键字快速查找,允许重复值容器适配器:接口转换 stack栈模型:先进后出queue队列模型:先进先出priotriy_queue优先级队列:最高优先级的先出向量(Vector)是一个封装了动态大小的数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。
vector 在尾部添加或者移除元素非常快,中间元素的操作非常耗时,因为需要移动元素。
优缺点:
检索效率高操作最后一个元素效率高操作中间元素效率差一旦需要重新分配空间,则性能很差list 容器是双向线性链表结构,指针将所有元素链接起来。
优缺点:
检索效率差,需要顺序查找,迭代器只能 ++,不能一次性跳转增删元素效率高stack 容器操作跟堆栈一样,都支持入栈和出栈。
set 是包含有序对象的关联容器,默认的顺序是从小到大的。
deque 是一个双端数组容器,可以在头部和尾部操作元素。
stack 容器操作跟堆栈一样,都支持入栈和出栈。
STL 包含一些独立函数(称为算法)用来操作容器。需要引入 algorithm 头文件后使用。
例如,find 查找算法,找到元素则返回元素的迭代器,否则返回改容器最后一个元素之后的位置(即 v.end())。copy 复制算法,将容器或其一部分复制到另一个容器中。
使用迭代器,可以对容器元素进行枚举遍历。迭代器定义在 中。
迭代器相当于指针,可以进行解引用(求出指针所指向变量的值,*p)和算术运算。
迭代器有4中不同声明方式,第一种最常用:
iterator:前项遍历,可读可写 const_iterator:前项遍历,只读 reverse_iterator:后向遍历,可读可写 const_reverse_iterator:后向遍历,只读假设 v 是某个实例化的容器,例如 vector<int> v(),则:
v.begin():返回指向容器首元素的迭代器 v.end():返回指向容器尾元素的下一个位置的迭代器 v.rbegin():返回指向容器最后一个元素的反向迭代器 v.rend():返回指向容器第一个元素之前的位置的反向迭代器转载于:https://www.cnblogs.com/kika/p/10851536.html
