C++
text::数据结构与算法
一、C语言 0 基础
单行注释://
多行注释:/**/
#include <stdio.h> #define 常量名 常量值 #define PI 3.14
#define x 5 #define a x+x cout << a * a << endl ;
1 变量 int a = 10 ;short b = 5 ;long c = 20 ;long long d = 12 ;float a = 10.12 ;double b = 12.24 ;bool a = true ;char a = 'a' ;char str[] = "123" ;int arr[] = {1 ,2 ,3 ,4 };char arr[] = {'a' ,'b' ,'c' };const 数据类型 name = value;unsigned int a = 10 ;
2 运算符
数据运算
算术运算符
术语
示例
结果
+
正号
+3
3
-
负号
-3
-3
+
加
10 + 5
15
-
减
10 - 5
5
*
乘
10 * 5
50
/
除
10 / 5
2
%
取模(取余)
10 % 3
1
++
前置递增
a=2; b=++a;
a=3; b=3;
++
后置递增
a=2; b=a++;
a=3; b=2;
—
前置递减
a=2; b=—a;
a=1; b=1;
—
后置递减
a=2; b=a—;
a=1; b=2;
赋值运算符
术语
示例
结果
=
赋值
a=2; b=3;
a=2; b=3;
+=
加等于
a=0; a+=2;
a=2;
-=
减等于
a=5; a-=3;
a=2;
*=
乘等于
a=2; a*=2;
a=4;
/=
除等于
a=4; a/=2;
a=2;
%=
模等于
a=3; a%2;
a=1;
比较运算符
术语
示例
结果
==
相等于
4 == 3
0
!=
不等于
4 != 3
1
<
小于
4 < 3
0
>
大于
4 > 3
1
<=
小于等于
4 <= 3
0
>=
大于等于
4 >= 1
1
逻辑运算符
术语
示例
结果
!
非
!a
如果a为假,则!a为真; 如果a为真,则!a为假。
&&
与
a && b
如果a和b都为真,则结果为真,否则为假。
`
`
或
`a
b`
如果a和b有一个为真,则结果为真,二者都为假时,结果为假。
&
按位与
a & b
a和b每一位做与运算,全真为1,否则为0
`
`
按位或
`a
b`
a和b每一位做或运算,全假为0,否则为1
^
按位异或
a ^ b
a和b每一位做异或运算,相同为1,相反为0
3 输入输出
格式化输出
转义字符
含义
ASCII 码值(十进制)
\a
警报
007
\b
退格(BS) ,将当前位置移到前一列
008
\f
换页(FF),将当前位置移到下页开头
012
\n
换行(LF) ,将当前位置移到下一行开头
010
\r
回车(CR) ,将当前位置移到本行开头
013
\t
水平制表(HT) (跳到下一个TAB位置)
009
\v
垂直制表(VT)
011
\\
代表一个反斜线字符”\”
092
'
代表一个单引号(撇号)字符
039
"
代表一个双引号字符
034
\?
代表一个问号
063
\0
数字0
000
\ddd
8进制转义字符,d范围0~7
3位8进制
\xhh
16进制转义字符,h范围0~9,a~f,A~F
3位16进制
scanf ("" );scanf ("%d" ,&a);printf ("" );printf ("%d" ,a);printf ("%d %d" ,a,b);
4 判断
执行满足条件的语句
if () {}else if () {}else {] switch (){ case 1 :break ; case 2 :break ; default : } c = a > b ? t : f;
5 循环
多次重复执行同一段语句
for (){}while (){}do {} while () break ;continue ;
6 数组
存放相同数据元素的集合
索引从0开始
int nums[10 ];char strs[10 ];int score[10 ] = { 0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 };int arr[2 ][3 ] = { {1 ,2 ,3 },{4 ,5 ,6 } };int lst[1 ][2 ][3 ];int score[] = { 0 ,1 ,2 ,3 ,4 };int arr[][3 ] = {{1 , 2 , 3 }, {4 , 5 , 6 }};nums[i]; nums[i][j];
7 函数
实现代码复用
形参:函数内部新建的参数。
实参:函数外传入的参数。
exit 函数:链接
void func () {}void func () ;int func () { return num; }func(); void swap (int num1, int num2) ;swap(a, b); void swap2 (int * p1, int *p2) ;swap2(&a, &b);
8 文件
持久化存储
FILE *fp = fopen("path" ,mode); fscanf (fp,"%d" ,&num);fgets(buf,size(buf),fp); fprintf (fp,"%d" ,num);fputs (buf,fp);fclose(fp);
9 指针
可以通过指针间接访问内存
指针所占地址:所有数据类型的指针,x86是4B,x64是8B
空指针:指针变量指向内存中编号为0的空间
野指针:指针变量指向非法的内存空间
int * p;p = &a; *p; int * p = NULL ;int *p = arr;
for (int i = 0 ; i < 10 ; i++){ printf ("%d" , *p); p++; }
10 结构体
将一组数据组合成一个整体
结构体指针使用:先要指向一个结构体
struct student { string name; int age; int score; }stu3; int main () { struct student stu1 ; struct student stu2 = { "李四" ,19 ,60 }; stu1.name = "张三" ; stu1.age = 18 ; stu1.score = 100 ; printf ("%d" ,stu1.age); } struct student arr [3]={ {"张三" ,18 ,80 }, {"李四" ,19 ,60 }, {"王五" ,20 ,70 } }; struct student stu = { "张三" ,18 ,100 , };struct student * p = &stu;p->score = 80 ; struct student { string name; int age; int score; }; struct teacher { int id; string name; int age; struct student stu ; }; struct teacher t1 ;t1.stu.name = "张三" ; void printStudent (const student *stu) ;
11 内存
malloc申请内存:
需要给定空间大小。并且malloc返回的是(void*),需要将指针强制转换成对应指针类型。
sizeof (int );cout << "short 类型所占内存空间为: " << sizeof (short ) << endl ;int *arr;arr = (int *)malloc (size * sizeof (int )); free (arr);
二、C++基础 0 工程包含
多个cpp包含同一个头文件。
头文件中有#pragma once且只有声明,不能有定义,否则会多次包含。
extern int a:全局声明。
int a:定义。
#ifndef EXAMPLE_H #define EXAMPLE_H #include <iostream> using namespace std;struct Point { int x; int y; }; class Rectangle {private : int width; int height; public : Rectangle (int w, int h); int getArea () ; }; int add (int a, int b) ;#endif
#include "example.h" Rectangle::Rectangle (int w, int h) { width = w; height = h; } int Rectangle::getArea () { return width * height; } int add (int a, int b) { return a + b; }
#include "example.h" int main () { Rectangle rect (5 , 3 ) ; int area = rect.getArea (); std::cout << "Area: " << area << std::endl; Point p; p.x = 10 ; p.y = 20 ; std::cout << "Point: (" << p.x << ", " << p.y << ")" << std::endl; int sum = add (2 , 3 ); std::cout << "Sum: " << sum << std::endl; return 0 ; }
1 基本使用 #include <iostream> using namespace std; int main () { cout << "Hello C++" << endl; } using std::endl;int main () { std::cout << "!" << endl; } #include <iostream> using namespace std;int main () { cout << __cplusplus << endl; }
2 变量
大部分同C语言。
float f1 = 3.14f ;std::string str2 = "" ; wchar_t unichar = L"Hello World!" ;
int num = stoi (str);string str = stirng ("123" ); string space = string (10 ,' ' ); string str = to_string (num); s.substr (int pos = 0 ,int n); s.find (str,position); str = a + b; str.append (c); unsigned int n = s.length ();
#include <iostream> #include <iomanip> using namespace std;int main () { float f = 123.45678901234567894567890123456789f ; std::cout << "float: " << std::fixed << std::setprecision (7 ) << f << std::endl; double d = 123.45678901234567894567890123456789 ; std::cout << "double: " << std::fixed << std::setprecision (16 ) << d << std::endl; return 0 ; }
3 运算符
同C。
4 输入输出
输入带空格
输入被跳过问题
using namespace std;cin >> (scanf) ; cout << (print) << endl; cout << "Hello" << " " << "World" << endl; char []带空格:cin.get (arr,len);string带空格:cin.ignore (); getline (cin,str);
5 判断
同C。
6 循环
同C。
label: goto label;for (auto x:str);for (auto & x:str);
7 数组
同C。
8 函数
同C。
默认参数
不同于python,默认参数之后的参数都必须是默认参数,输入值时不可以指定参数的位置(例不可以:fun( b = 10);
)
函数在声明和实现中只能有一处有默认参数。
占位参数
传值时必须传值,但是该值不会被使用(暂时)(例:void fun ( int a , int )
)
占位参数也可以是默认参数(例:void fun ( int a , int = 10 )
)
函数重载
参数是( int& a) 和 ( const int& a ):func(a)是第一个,func(10)是第二个。
默认参数容易出现二义性。
void myFunction (int a, int b = 5 , int c = 10 ) {}myFunction (1 ); myFunction (1 , 2 ); myFunction (1 , c = 15 ); void myFunction (int a, int b = 5 ) ; void myFunction (int a, int b) {}void myFunction (int a, int ) { } myFunction (5 , 10 ); void myFunction (int a, int b = 10 ) {}myFunction (5 ); void myFunction (int & a) {}void myFunction (const int & a) {}int main () { int x = 5 ; const int y = 10 ; myFunction (x); myFunction (y); myFunction (10 ); return 0 ; } void myFunction (int a, int b = 5 ) {}void myFunction (int a, int b) {}myFunction (1 );
9 文件
流对象
作用
ofstream
写文件
ifstream
读文件
fstream
读写文件
打开方式
解释
ios::in
读
ios::out
写
ios::ate
初始位置为文件尾
ios::app
追加
ios::trunc
文件存在则删除文件再创建
ios::binary
二进制打开
文件打开方式可以配合使用,如二进制写:ios::binary | ios::out
#include <fstream> ifstream ifs; ifstream ifs ("path" ,mode) ;ofstream ofs; ofstream ofs ("path" ,mode) ;ofs.open ("path" ,mode); ifs >> buf; ofs << "data" << endl; ofs.close (); ifs.read (buf,sizeof (buf)); ifs.getline (buf, sizeof (buf)); getline (ifs,buf);char c = ifs.get ();ofs.write (buf,sizeof (buf));
10 异常
链接
throw "tip" ;try {} catch (ExceptionName e){} catch (...){}
11 指针
同C
const int * p = &a;p1 = &b; *p1 = 100 ; int * const p = &a;p2 = &b; *p2 = 100 ; const int * const p = &a;p3 = &b; *p3 = 100 ;
12 结构体
同C
默认访问权限是公有。
13 内存
同C
内存区域
解释
代码区
存放函数体的二进制代码,由操作系统进行管理
全局区
存放全局变量 全局常量和静态变量
栈区
由编译器自动分配释放,存放函数的参数值,局部变量,局部常量等(栈区函数使用完后返回的局部变量会保留一次,cout输出一次其他字符串后会被清空)
堆区
由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
type *p = new type ( num ) ; type *p = new type[ num ] ; delete p; delete [] p;
三、 引用
注意:
引用必须初始化,不能只有定义。(作为函数参数可以补初始化)
引用初始化后不能再修改指向对象,之后使用等于是赋值。(包括地址指向)
type &alias = name; int & b = a;int & ref = a; int * const ref = &a;
int add (int & a,int & b) ;int & add () ;int & fun () { static int a = 10 ; return a; } int main () { int & ref = fun (); fun () = 1000 ; std::cout << ref << std::endl; return 0 ; }
四、 类与对象 1 封装
class name { access : type / function };
: class 类名 { 访问权限 : 属性 / 方法 } ;(不用于C#,一个访问权限会影响后面所有的属性和方法)
class_name::function
:class_name.function,直接使用类的方法,不实例化。
类的拆分:
头文件:保留类名和函数名。
主文件:类的具体实现。(不用留下类名,而是在包含头文件后 class_name : : function 即可,相当于直接用类的属性或方法)
访问权限:(类内属性方法默认私有,结构体内属性方法默认公有)
public:类内可以访问,类外可以访问。
private:类内可以访问,类外不可以访问。不可以被继承。
protected:类内可以访问,类外不可以访问。可以被继承。
#include <iostream> using namespace std;class Person {private : string name; int age; int high = 160 ; }; public : int & age = high; Person (string n, int a) { name = n; age = a; } void say_hello () { cout << "Hello, my name is " << name << " and I am " << age << " years old." << endl; } void set_age (int a) { age = a; } int get_age () { return age; } }; int main () { Person p ("Bob" , 30 ) ; p.say_hello (); p.set_age (40 ); cout << "Bob's age is " << p.get_age () << endl; return 0 ; }
2 对象特征 2.1 构造和析构
构造函数:class_name() {}
,实例化对象,可以有参数。
初始化列表: class_name() : type() , ... ;
(同C#,自动调用构造函数)
初始化列表的执行顺序与成员变量在类定义中的声明顺序有关。
建议成员变量的初始化顺序应该与它们在类定义中的声明顺序一致,以避免潜在的问题。
初始化类:
括号法(无参直接不写括号,不然是声明函数)
显示法(不写实例是初始化一个匿名对象,不使用会立刻被释放。且不要匿名初始化拷贝函数,编译器会认为是对象声明(Person(p3)等价于Person p3 ))
析构函数:~class_name() {}
,销毁对象,不可以有参数。
类嵌套,构造类先构造自身中的其他类再构造自身,析构则相反。
Person p (10 ) ;Person p; Person p = Person (); Person p4 = 10 ; if (p != NULL ) { delete p; p = NULL ; }; class MyClass {private : int m_value1; int m_value2; public : MyClass (int value1, int value2) : m_value1 (value1), m_value2 (value2) { std::cout << "Hello World" << std::endl; } };
#include <iostream> class Person {public : Person () { std::cout << "Person constructor called" << std::endl; } Person (const Person& other) { std::cout << "Person copy constructor called" << std::endl; } ~Person () { std::cout << "Person destructor called" << std::endl; } }; int main () { Person (); Person (p3); Person p4; return 0 ; }
2.2 拷贝类
浅拷贝:简单的复制拷贝(指针则是复制地址)。
深拷贝:重新在堆区申请空间再进行拷贝。
类构造函数分类:有参和无参,普通和拷贝构造函数(Person ( const Person &p) ;
,传一个自己的引用)
拷贝构造函数使用场景:
函数传局部变量:当将一个对象作为参数传递给函数时,拷贝构造函数会在函数调用期间创建该对象的副本。
函数 return:当函数返回一个对象时,拷贝构造函数会在函数返回之前创建该对象的副本。
主动拷贝:有时候需要显式地创建一个对象的副本,而不是与现有对象共享数据。
注意:如果用户定义了拷贝构造函数,c++不会提供任何默认构造函数。
class MyClass {private : char * m_data; public : MyClass (const MyClass& other) : m_data (other.m_data) {} } class MyClass {private : char * m_data; public : MyClass (const MyClass& other) { m_data = new char [strlen (other.m_data) + 1 ]; strcpy (m_data, other.m_data); } } MyClass obj2 (obj1) ;MyClass obj3 = obj1;
#include <iostream> class MyClass {public : MyClass () { std::cout << "Constructor called" << std::endl; } MyClass (const MyClass& other) { std::cout << "Copy constructor called" << std::endl; } }; void doSomething (MyClass obj) { } int main () { MyClass obj; doSomething (obj); return 0 ; } #include <iostream> class MyClass {public : MyClass () { std::cout << "Constructor called" << std::endl; } MyClass (const MyClass& other) { std::cout << "Copy constructor called" << std::endl; } }; MyClass createObject () { MyClass obj; return obj; } int main () { MyClass newObj = createObject (); return 0 ; } #include <iostream> class MyClass {private : int m_value; public : MyClass (int value) : m_value (value) { std::cout << "Constructor called" << std::endl; } MyClass (const MyClass& other) : m_value (other.m_value) { std::cout << "Copy constructor called" << std::endl; } }; int main () { MyClass obj1 (42 ) ; MyClass obj2 = obj1; return 0 ; }
2.3 静态类
静态成员变量:在类内定义(不可初始化),在类外全局处(不可以在函数中)初始化(不赋值默认0),初始化案例: int test::c ;
静态成员函数:只可以访问静态变量。
#include <iostream> using namespace std;class Test {public : static int count; static void incrementCount () { count++; } }; int Test::count = 0 ; int main () { cout << "Count: " << Test::count << endl; Test::incrementCount (); Test::incrementCount (); cout << "Count: " << Test::count << endl; Test::incrementCount (); cout << "Count: " << Test::count << endl; return 0 ; }
2.4 类内存
内存:空对象为1B,非空对象成员变量和是多少内存就是多少。
空对象实例化为1B,空对象指针实例化为4B
有虚函数的类对象中都有一个虚函数表指针 __vptr,大小为4B(32位机器) / 8B(64位机器)
成员函数和静态成员不包含在其中(分开存储)。
this指针指向被调用的成员函数所属的对象。return *this;
可以返回自身(函数返回值是 class_name&
)。
空指针可以访问成员函数,但是下列情况会异常:
遇到 this会异常(因为指针为0) (可以添加代码来避免:if( this == NULL) { return ; })。
遇到虚函数会直接异常(因为找不到虚函数表)
#include <iostream> class MyClass {private : string name = "123" ; public : void printMessage () { cout << "Hello, I am MyClass!" << this ->name << endl; } virtual void printMessage2 () { cout << "Hello, I am MyClass!" << endl; } }; int main () { MyClass* ptr = nullptr ; ptr->printMessage (); ptr->printMessage2 (); return 0 ; }
2.5 常值类
常函数:void add() const {};
。不可以在函数内修改类的值(相当于:const Class * const this
),不会调用其他非const的成员函数。但若加上 mutable 的属性则可以在常函数中修改值。
常对象:const Class name;
。只可以调用常函数。
#include <iostream> class MyClass {public : int value; void setValue (int newValue) { value = newValue; } void printValue () const { std::cout << "Value: " << value << std::endl; } }; int main () { const MyClass obj; obj.printValue (); return 0 ; }
2.6 友元
在类外访问类内的私有属性或方法。
友好函数:在类中用 friend type funtion();
声明类外的全局函数即可。
友好类:在类中用friend class name;
声明类外的其他类即可。
友好类函数:在类中用 friend type class_name::function();
声明类外的其他类中的函数即可。
#include <iostream> class MyClass {private : int privateData; public : MyClass (int data) : privateData (data) {} friend void friendFunction (const MyClass& obj) ; friend class FriendClass ; }; void friendFunction (const MyClass& obj) { std::cout << "Friend function accessing private data: " << obj.privateData << std::endl; } class FriendClass {public : void accessPrivateData (const MyClass& obj) { std::cout << "Friend class accessing private data: " << obj.privateData << std::endl; } }; int main () { MyClass obj (42 ) ; friendFunction (obj); FriendClass friendObj; friendObj.accessPrivateData (obj); return 0 ; }
3 运算符重载
自定义的数据类型进行运算。
type operator +() {}; type operator -() {}; type operator *() {}; type operator /() {}; type operator <<( ostream& cout , class & p ) { ... }; ostream& operator <<( ostream& cout , class & p ){ return cout; }; class & operator ++() { this .num ++; return *this ; } class operator ++( int ) { class temp = *this ; this .num++; return temp; } class operator =( class & p ) { return *this ; } bool operator ==( class & p ) { ... }bool operator >( class & p ) { ... }bool operator <( class & p ) { ... }type operator ( ... ) { ... }
#include <iostream> class MyClass {private : double data; public : MyClass (double val) : data (val) {} double operator +(double operand) const { return data + operand; } double getData () { return data; } }; double operator +(MyClass operand1, MyClass operand2) { return operand1.getData () + operand2.getData (); } int main () { MyClass obj (2.0 ) ; double res1 = obj + 1.0 ; std::cout << "Member function overload result: " << res1 << std::endl; double res2 = 1.0 + 2.0 ; std::cout << "Global function overload result: " << res2 << std::endl; return 0 ; }
4 继承
继承方式:
基础继承:class child: public father [ ,public father ...]
(class 派生类 : 继承方式 基类)
公共继承:public(可以访问父类公共的东西,且权限不变)
保护继承:protected(可以访问父类公共和受保护的东西,且权限变为受保护)
私有继承:private(可以访问父类公共和受保护的东西,且权限变为私有)
继承特性:
菱形继承问题定义:两个派生类继承了同一个基类,然后又有一个派生类继承了这两个派生类。
使用属性时通过作用域区分。
利用虚继承,在两个派生类继承方式之前加上关键字 virtual(class student : virtual public people),(此时继承下来的东西为 { vbptr }(virtual base pointer:虚基类指针),直接指向第一份数据,在第三个派生生继承时就不会产生二义性)
#include <iostream> using namespace std;class Father {public : int publicData; void publicFunction () { cout << "Father's public function" << endl; } protected : int protectedData; void protectedFunction () { cout << "Father's protected function" << endl; } private : int privateData; void privateFunction () { cout << "Father's private function" << endl; } }; class Child_Public : public Father {public : void accessFatherData () { cout << "Accessing father's public data: " << publicData << endl; } }; class Child_Protected : protected Father {public : void accessFatherData () { cout << "Accessing father's protected data: " << protectedData << endl; } }; class Child_Private : private Father {public : void accessFatherData () { } }; int main () { Child_Public child1; child1.publicData = 42 ; child1.accessFatherData (); child1.publicFunction (); Child_Protected child2; child2.accessFatherData (); Child_Private child3; child3.accessFatherData (); cout << "child publicData data:" << child1.publicData << endl; cout << "father publicData data:" << child1.Father::publicData << endl; return 0 ; }
#include <iostream> class Base {public : int data; Base (int val) : data (val) {} }; class Derived1 : virtual public Base {public : Derived1 (int val) : Base (val) {} }; class Derived2 : virtual public Base {public : Derived2 (int val) : Base (val) {} }; class Derived3 : public Derived1, public Derived2 {public : Derived3 (int val) : Base (val), Derived1 (val), Derived2 (val) {} }; int main () { Derived3 obj (42 ) ; std::cout << "Data from Derived1: " << obj.Derived1::data << std::endl; std::cout << "Data from Derived2: " << obj.Derived2::data << std::endl; std::cout << "Data from Derived3: " << obj.data << std::endl; return 0 ; }
5 多态 5.1 分类
静态多态:函数重载,运算符重载,复用函数名。
动态多态:派生类,虚函数。
静态多态的函数地址早绑定:编译阶段确定函数地址。
动态多态的函数地址晚绑定:运行阶段确定函数地址。
#include <iostream> using namespace std;class Animal {public : void speak () { cout << "Animal speaks" << endl; } }; class Cat : public Animal {public : void speak () { cout << "Cat speaks" << endl; } }; void speak (Animal* animal) { animal->speak (); } int main () { Cat cat; speak (&cat); return 0 ; } #include <iostream> using namespace std;class Animal {public : virtual void speak () { cout << "Animal speaks" << endl; } }; class Cat : public Animal {public : void speak () { cout << "Cat speaks" << endl; } }; void speak (Animal* animal) { animal->speak (); } int main () { Cat cat; speak (&cat); return 0 ; }
5.2 动态多态条件
原理:存在虚函数表,虚函数起始位置存在虚函数表中。子类未重写则直接继承虚函数表,若重写了则虚函数表中对应函数地址要改成子类的。
有继承关系。
子类重写父类的虚函数。
5.3 纯虚函数与抽象类
纯虚函数:virtual void func() = 0;
抽象类:只要类中有一个纯虚函数就为抽象类。
抽象类特点:
无法实例化对象。
抽象类的子类必须重写父类的纯虚函数,也就是要有具体实现,否则子类也是抽象类。
#include <iostream> class Shape {public : virtual void draw () = 0 ; void print () { std::cout << "Print shape." << std::endl; } }; class Circle : public Shape {public : void draw () override { std::cout << "Draw circle." << std::endl; } }; int main () { Circle circle; circle.draw (); circle.print (); return 0 ; }
5.4 虚析构和纯虚析构
父类指针在析构时,不会调用子类中的析构函数,导致子类中堆区内容释放不干净。所以子类中有堆区内容时需要写虚析构或纯虚析构,没有可以不写。
利用虚析构可以解决父类指针释放时子类释放不干净的问题:virtual ~father(){}
(写在父类中,要具体实现)
纯虚析构:
父类中声明:virtual ~father() = 0;
类外实现:father::~father(){}
有了纯虚析构之后,这个类也属于抽象类。
虚析构和纯虚析构只需要写一个即可。
6 VS 开发人员命令提示工具 :: x64 Native Tools Command Prompt for VS 2022 cd <path>cl /d1 reportSingleclassLayout<name> <name>.cpp(cl :l是字母,d1:1是数字)
五、 模板 1 基本概念
建立通用的模具,大大提高复用性。
函数模版(T类型须一致,必须确定T才可以使用):
template < typename T >
type function ( T a ) {} ;
使用:
自动推导;
显式指定 function<>() ;
与普通函数区别:
普通函数可以隐式转换(例:char -> int )
函数模板自动类型推导不会隐式转换,用显式指定类型则会发生隐式转换
局限性:
无法简单地比较、赋值数组、类这样的数据类型。解决方法可以选择运算符重载(一般不选)、直接对模板函数进行重载,特殊处理指定的类。
#include <iostream> template <typename T>T maximum (T a, T b) { return (a > b) ? a : b; } int main () { int num1 = 10 ; int num2 = 20 ; std::cout << "较大的数是:" << maximum (num1, num2) << std::endl; double num3 = 3.14 ; double num4 = 2.71 ; std::cout << "较大的数是:" << maximum (num3, num4) << std::endl; return 0 ; } template <typename T>T maximum (T a, T b) { return (a > b) ? a : b; } template <> Person& maximum (Person &a,Person &b) { return (a.age > b.age) ? a : b; }
2 与普通函数发生重载
如果函数模板和普通函数都可以调用,优先调用普通函数。
可以通过空模板参数列表强制调用函数模板。
函数模板可以发生函数重载。
如果函数模板可以产生更好的匹配,优先调用函数模板。
注:实际开发中不要同时出现函数模板和普通函数的重载。
#include <iostream> using namespace std;void myPrint (int a, int b) { cout << "Normal function called!" << endl; } template <typename T>void myPrint (T a, T b) { cout << "Template function called!" << endl; } template <typename T>void myPrint (T a, T b, T c) { cout << "Template overload function called!" << endl; } int main () { int num1 = 10 ; int num2 = 20 ; char char1 = 'a' ; char char2 = 'b' ; char char3 = 'c' ; myPrint (num1, num2); myPrint<>(num1, num2); myPrint (char1, char2, char3); myPrint (char1, char2); }
3 类模板
类模板与函数模板区别:
类模板没有自动类型推导的使用方式。
类模板在模板参数列表可以有默认参数。
类中成员函数创建时机:
普通类在一开始成员函数就会被创建。
模板类的成员函数只有在被调用时才会被创建(要知道 T 的值才会创建函数)。
template <class NameType ,class AgeType >class Person { private : NameType m_Name; AgeType m_Age; public : Person (NameType name,AgeType age) { this ->m_Name = name; this ->m_Age = age; } }; Person<string, int > p ("Ami" , 123 ) ;template <class NameType , class AgeType = int >class Person{}void printPerson1 (Person<string,int > &p){}template <class T1,class T2>void printPerson2 (Person<T1,T2> &p){} template <class T>void printPerson3 (T &p){}
4 类模板其他问题
类模板继承:
子类继承的父类是类模板时,子类在声明时要指明父类中T的类型。如果不指定编译器无法给子类分配内存。
如果想灵活指定父类T的类型,子类也需要变成类模板。
template <class T >class Base { T m; };class Son :public Base<int >{}; template <class T1 ,class T2 >class Son2 :public Base<T2>{ T1 obj;};
六、 STL技术 1 基本概念
STL基础知识:
STL(Standard Template Library,标准模板库)
STL从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)。
容器和算法之间通过迭代器进行无缝连接。
STL几乎所有的代码都采用了模板类或者模板函数。
STL六大组件:
容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
算法:各种常用的算法,如sort、find、copy、for_each等。
迭代器:扮演了容器与算法之间的胶合剂。
仿函数:行为类似函数,可作为算法的某种策略。
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
空间配置器:负责空间的配置与管理。
容器:
序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系。
算法:
质变算法:运算过程中会更改区间内的元素。
非质变算法:运算过程中不会更改区间内的元素。
迭代器:
算法需要靠迭代器才能访问容器中的元素。
每个容器有自己专属的地带器。功能类似指针。
常用的容器中的迭代器冲类为双向迭代器和随机访问迭代器
种类
功能
支持运算
输入迭代器
数据只读
只读,支持++、==、!=
输出迭代器
数据只写
只写,支持++
前向迭代器
读写,并向前推进迭代器
读写,支持++、==、!=
双向迭代器
读写,可前后推进迭代器
读写,支持++、—
随机访问迭代器
读写,可跳跃式访问任意数据
读写,支持++、—、[n]、-n、<、<=、>、>=
2 迭代器 vector<T>::iterator itBegin = v.begin (); vector<T>::iterator itEnd = v.end (); while (itBegin != itEnd){ cout << *itBegin << endl; itBegin++; } for (vector<int >::iterator it = v.begin ();it != v.end ();it++){ cout << *it << endl; } #include <algorithm> void func (int val) { cout << val << endl; }void main () { for_each(itBegin, itEnd, func); }(*it).name; it->name; vector<Person *> v; Person p ('Bob' ,12 ) ;v.push_back (&p1); for (vector<Person *>::iterator it = v.begin ();it != v.end ();it++){ cout << (*it)->name << endl; cout << (**it).name << endl; } deque<int >const_iterator it;
deque<int > dep; dep.push_back (1 ); dep.push_back (2 ); deque<int >::iterator beg = dep.begin (); deque<int >::iterator end = dep.end (); dep.push_back (3 ); dep.push_back (4 ); dep.push_back (5 ); cout << (*end) << endl;
3 vector容器
vector类似于数组,也成为单端数组。
数组空间是静态的,而vector可以动态扩展。扩展方式是找到更大的内存空间,然后将原数据拷贝新空间,释放原空间。
vector容器的迭代器是支持随机访问的迭代器。
#include <iostream> #include <vector> using namespace std;vector<T> v; vector (v.begin (),v.end ()); vector (n,elem); vector (const vector &vec); vector& operator = (const vector &vec); assign (beg,end); assign (n,elem); empty (); capacity (); size (); resize (int num); resize (int num,elem); push_back (elem); pop_back (); insert (const_iterator pos,elem); insert (const_iterator pos,int count,elem); erase (const_iterator pos); erase (const_iterator start,const_iterator end); clear (); at (int idx); operator []; front (); back (); swap (vec); reserve (int len); vector<vector<int >> v; vector<int > v1; vector<int > v2; v.push_back (v1); v.push_back (v2); for (vector<vector<int >>::iterator it = v.begin (); it != v.end (); it++){ for (vector<int >::iterator vit = (*it).begin (); vit != (*it).end (); vit++) { cout << *vit << " " ; } cout << endl; }
int main () { vector<int > v; for (int i = 0 ; i < 10000 ; i++) { v.push_back (i); } cout << "before swap" << endl; cout << "capacity:" << v.capacity () << endl; cout << "size:" << v.size () << endl; v.resize (3 ); cout << "after resize" << endl; cout << "capacity:" << v.capacity () << endl; cout << "size:" << v.size () << endl; vector <int >(v).swap (v); cout << "after swap" << endl; cout << "capacity:" << v.capacity () << endl; cout << "size:" << v.size () << endl; } vector<vector<int >> arr (rowLen, vector <int >(colLen, 0 ));
4 string容器
字符串。
string本质是一个类,内部封装了char*,有很多方法可以管理这个char*。
string (); string (const char * s); string (const string& str); string (int n,char c); string& operator =(const char * s); string& operator =(const string &s); string& operator =(char c); string& assign (const char *s) ; string& assign (const char *s,int n) ; string& assign (const string &s) ; string& assign (int n,char c) ; string& operator +=(const char * str); string& operator +=(const char c); string& operator +=(const string& str); string& append (const char *s) ; string& append (const char *s, int n) ; string& append (const string &s) ; string& append (const string &sint pos,int n) ; int find (const string& str, int pos = 0 ) const ; int find (const char * s,int pos = 0 ) const ; int find (const char * s,int pos,int n) const ; int find (const char c, int pos = 0 ) const ; int rfind (const string& str, int pos = npos ) const ; int rfind (const char * s,int pos = npos ) const ; int rfind ( const char * s,int pos,int n) const ; int rfind (const char c,int pos = 0 ) const ; string& replace (int pos,int n,const string& str) ; string& replace (int pos,int n,const char * s) ; int compare (const string &s) const ;int compare (const char *s) const ;char & operator [](int n); char & at (int n) ;string& insert (int pos,const char * s) ; string& insert (int pos,const string& str) ; string& insert (int pos,int n, char c) ; string& erase (int pos, int n = npos) ;string substr (int pos = 0 ,int n = npos) const ;
5 deque容器
双端队列。
vector对于头部的插入删除效率低,因为插入要移动元素。
deque相对而言,对头部的插入删除速度比vector快。
vector访问元素时的速度会比deque快,这和两者内部实现有关。
内部工作原理:deque内部有个中控器,中控器中有多段缓冲区,缓冲区中存放数据。中控器维护每段缓冲区中的地址,使得deque使用时像一片连续的内存空间。
deque容器的迭代器是支持随机访问的迭代器。
#include <deque> deque<T> deqT; deque (v.begin (),v.end ());deque (n,elem);deque (const deque &deq);deque& operator = (const deque &deq); assign (beg,end);assign (n,elem);empty ();size ();resize (int num);resize (int num,elem);push_back (elem);push_front (elem);pop_back ();pop_front ();insert (pos,elem);insert (pos,n,elem);insert (pos,beg,end);erase (pos);erase (beg,end);clear ();at (int idx);operator [];front ();back ();
6 stack容器
栈。
#include <stack> stack<T> stk; stack (const stack &stk);stack& operator =(const stack &stk); empty ();size ();push (elem);pop (); top ();
7 queue容器
普通队列。
#include <queue> queue<T> que; queue (const queue &que);queue& operator =(const queue &que); empty ();size ();push (elem);pop ();front (); back (); empty ();size ();resize (int num);resize (int num,elem);
priority_queue<T> maxHeap; priority_queue<T, vector<T>, less<T>> maxHeap; priority_queue<T, vector<T>, greater<T>> minHeap; maxHeap.size (); maxHeap.empty (); maxHeap.push (val); maxHeap.pop (); maxHeap.top ();
8 list容器
双向循环链表。
list的优点:
采用动态存储分配,不会造成内存浪费和溢出。
链表增删时间为O(1)。
list的缺点:
空间(额外的指针指向下一节点)和时间(遍历)消费大。
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
list容器的迭代器是双向迭代器。
#include <list> list<T> lst; list (beg,end);list (n,elem);list (const list &lst);list& operator = (const list &list); assign (beg,end);assign (n,elem);push_back (elem);push_front (elem);pop_back ();pop_front ();insert (pos,elem);insert (pos,n,elem);insert (pos,beg,end);erase (pos);erase (beg,end);clear ();remove (elem); front ();back ();reverse (); sort ([func]); swap (lst);
9 set/multiset容器
集合。
所有元素在插入时自动排序。
本质:关联式容器,底层是二叉树。
set不允许有重复元素,multiset允许有重复元素。
#include <set> set<T> st; set (const set &st);multiset<T> st; set& operator =(const set &st); empty ();size ();insert (elem);erase (pos);erase (beg,end);erase (elem); clear ();find (key); count (key); swap (st);multiset<T> st; multiset (const set &st);pair<type,type> p (val_1,val_2) ;pair<type,type> p = make_pair (val_1,val_2); p.first; p.second;
pair<set<int >::iterator,bool > ret = s.insert (10 ); if (ret.second) { cout << "success!" << endl;}else { cout << "failed!" << endl;}class MyCompare { public : bool operator () (int v1,int v2) { return v1 > v2; } } void main () { set<int ,MyCompare> s; }#include <iostream> #include <algorithm> #include <set> using namespace std;class Person { public : string m_Name; int m_Age; Person (string name, int age) { this ->m_Name = name; this ->m_Age = age; } }; class comparePerson { public : bool operator () (const Person& v1, const Person& v2) const { return v1.m_Age > v2.m_Age; } }; int main () { set<Person, comparePerson> s; s.insert (Person ("lili" , 16 )); s.insert (Person ("muge" , 12 )); s.insert (Person ("sora" , 15 )); for (set<Person, comparePerson>::iterator it = s.begin (); it != s.end (); it++) { cout << (*it).m_Name << " is " << (*it).m_Age << endl; } return 0 ; }
10 map/multimap容器
字典。
元素都是pair,第一个是key,第二个是value。
所有元素都会根据元素的键值自动排序。
本质:关联式容器,底层是二叉树。
map不允许有重复的键,multimap允许有重复的键。
自定义数据类型需要指定排序规则规则(同理set)
#include <map> map<T1,T2> mp; map (const map &mp);unordered_map<T1,T2> mp; multimap<T1,T2> mp; map& operator =(const map &mp); empty ();size ();insert (pair<key,value>);erase (pos);erase (beg,end);erase (key); clear ();find (key); count (key); swap (mp);
class MyCompare { public : bool operator () (int v1,int v2) { return v1 > v2; } } void main () { map<int ,int ,MyCompare> mp; }map<int ,int > mp; mp[i]++; for (map<int ,int >::iterator iter = mp.begin (); iter != mp.end (); iter++){ int key = iter->first; int second = iter->second; } if (mp.find (s[i]) != mp.end ()){;}
11 函数对象
概念:
重载函数调用操作符的类,其对象常称为函数对。
函数对象使用重载的()时,行为类似函数调用。也叫仿函数。
本质:
特点:
函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值函数。
对象超出普通函数的概念,函数对象可以有自己的状态。
函数对象可以作为参数传递。
谓词概念:
返回bool类型的仿函数称为谓词。
如果operator()接受一个参数,那么叫一元谓词。
如果operator()接受两个参数,那么叫二元谓词。
class MyAdd { public : int operator () (int v1,int v2) { return v1 + v2; }}; void test01 () { MyAdd myAdd; cout << myAdd (10 ,10 ) << endl; } class MyPrint { public : int count = 0 ; void operator () (string test) { cout << test << endl; count++; } }; void test02 () { MyPrint myPrint; for (int i = 0 ; i < 10 ; i++) { myPrint ("Hello World!" ); } cout << "myPrint has been called " << myPrint.count << " times" << endl; } void doPrint (MyPrint &mp, string test) { mp (test); }void test03 () { MyPrint myPrint; doPrint (myPrint,"Hello C++" ); }
#include <functional> template <class T > T plus<T> template <class T > T minus<T> template <class T > T multiplies<T> template <class T > T divides<T> template <class T > T modulus<T> template <class T > T negate<T> template <class T > bool equal_to<T> template <class T > bool not_equal_to<T> template <class T > bool greater<T> template <class T > bool greater_equal<T> template <class T > bool less<T> template <class T > bool less_equal<T> template <class T > bool logical_and<T> template <class T > bool logical_or<T> template <class T > bool logical_not<T>
#include <iostream> #include <functional> int main () { std::greater<int > greaterThan; int a = 5 ; int b = 10 ; if (greaterThan (a, b)) { std::cout << "a is greater than b" << std::endl; } else { std::cout << "a is not greater than b" << std::endl; } return 0 ; }
七、 STL中的算法
所有不支持随机访问迭代器的容器,不可以用标准算法。(例如:list)
1 遍历算法 #include <algorithm> for_each(iterator beg,iterator end,_func); for_each(v.begin (),v.end (),print01); for_each(v.begin (),v.end (),print02 ()); transform (iterator ori_beg,iterator ori_end,iterator new_beg,_func);
class NewData {private : static int i; public : int operator () () { this ->i++; return i * 10 ; } }; int NewData::i = 0 ;class AddFive {public : int operator () (int value) const { return value + 5 ; } }; class MyPrint {public : void operator () (int value) const { std::cout << value << std::endl; } }; int main () { vector<int > v; v.resize (10 ); NewData newData; generate (v.begin (), v.end (), newData); vector<int > news; news.resize (v.size ()); transform (v.begin (), v.end (), news.begin (), AddFive ()); for_each(news.begin (), news.end (), MyPrint ()); cout << news.capacity () << " " << news.size () << endl; return 0 ; }
2 查找算法 #include <algorithm> iterator find (iterator beg,iterator end,value) ;iterator find_if (iterator beg,iterator end, _Pred) ;iterator adjacent_find (iterator beg,iterator end) ;bool binary_search (iterator beg,iterator end,value) ;int count (iterator beg,iterator end,value) ;int count_if (iterator beg,iterator end,_Pred) ;
3 排序算法 #include <algorithm> sort (iterator beg,iterator end[,_Pred]);sort (v.begin (),v.end (),greater <int >()); srand ((unsigned int )time (NULL ));random_shuffle (iterator beg,iterator end);merge (iterator beg_1,iterator end_1,iterator beg_2,iterator end_2,iterator dest);reverse (iterator beg,iterator end);
4 拷贝替换算法 #include <algorithm> copy (iterator beg,iterator end,iterator dest);repalce (iterator beg,iterator end,old_value,new_value);replace_if (iterator beg,iterator end,_pred,new_value);swap (container c1,container c2);
5 算术生成算法 #include <numeric> int sum = accumulate (iterator beg,iterator end,value); int sum = accumulate (vec.begin () ,vec.end () ,1 ,multiplies <int >()); fill (iterator beg,iterator end,value);
6 集合算法 #include <algorithm> iterator set_intersection (iterator beg_1,iterator end_1,iterator beg_2,iterator end_2,iterator dest) ;iterator set_union (iterator beg_1,iterator end_1,iterator beg_2,iterator end_2,iterator dest) ;iterator set_difference (iterator beg_1,iterator end_1,iterator beg_2,iterator end_2,iterator dest) ;
vTarget.resize (min (v1.size (),v2.size ())); vector<int >::iterator itEnd = set_intersection (v1.begin (),v1.end (),v2.begin (),v2.end (),vTarget); for_each(vTarget.begin (),itEnd,myPrint); vTarget.resize (v1.size () + v2.size ()); vTarget.resize (max (v1.size (),v2.size ()));
八、C++库
九、C/C++应用