C++程序设计——知识点总结
C++程序设计课程的总结,方便以后快速查阅和复习
Week 2 从C走进C++
函数指针
函数名是函数的入口地址,指向函数的指针称为“函数指针”。
比如,qsort库函数:
void qsort(void *base, int nelem, unsigned int width,
int ( * pfCompare)( const void *, const void *));
其中的第四个参数,就是一个函数指针,pfCompare:比较函数的地址。
命令行参数
int main(int argc, char *argv[]){
...
}
argc:命令行参数的个数
argv:指针数组,argv[0]指向第一个命令行参数,argv[1]指向第二个命令行参数...
位运算
& | │ | ^ | ~ | << | >> |
---|---|---|---|---|---|
按位与 | 按位或 | 按位异或 | 按位非 | 左移 | 右移 |
异或运算实现a,b值交换:
a = a ^ b;
b = b ^ a;
a = a ^ b;
引用
变量的引用,等价于这个变量,相当于一个别名:
int n = 4;
int &r = n;
const关键字和常量
在定义前加上const关键字,就代表这是个常量(常量,常量指针,常引用),不可变的东西。
const int *p = &n; //常量指针
动态内存分配
用new分配内存,delete释放内存
分配变量:
int * p = new int;
* p = 5;
delete p;
分配数组:
int * p = new int[20];
p[0] = 1;
delete [ ] p;
内联函数,函数重载,缺省参数
-
内联函数:函数的定义前加关键字inline。
-
函数重载:名字相同,参数个数或类型不同。
-
缺省参数:定义函数时,让最右边的几个参数有缺省值,这样在调用的时候相应位置可以不写参数。
void func( int x1, int x2 = 2, int x3 = 3) { }
类的可访问范围
关键字:private,public,protected,缺省为私有成员
Week 3 类和对象
内联成员函数和重载成员函数
- 内联成员函数:inline + 成员函数,在类定义内部
- 成员函数的重载和参数缺省,使用缺省参数时注意避免有函数重载时的二义性
构造函数
- 成员函数的一种,名字与类名相同,可以有参数,不能有返回值
- 用来对对象进行初始化
- 若没有自定义,则默认生成无参构造函数
复制构造函数
用来复制的构造函数,这样一个参数:同类对象的引用
Complex( const Complex & c ) {...} //可以没有const
- 编译器会自动生成复制构造函数用于复制
- 若自定义了,则默认的复制构造函数不存在
- 复制构造函数起作用的三种情况:
- 用一个对象初始化同类的另一个对象
- 一个包含类A为参数的函数被调用时
- 函数的返回值是对象,函数返回时
类型转换构造函数
和类型转换函数不是一个名词的样子,这个类型转换构造函数,其实就是一个构造函数而已,但是像具有类型转换的功能,把其他的东西转换为本类,比如:
Complex(int i){
real = i; imag = 0;
}
析构函数
- 名字与类名相同,在前面加 ~,没有参数和返回值,比如:
~String();
- 一个类最多只有一个析构函数
- 对象消亡时自动调用
- 若自定义了,则不生成缺省析构函数
- delete运算可导致析构函数调用,比如:
delete pTest; delete [] pTest;
静态成员变量和静态成员函数
- 在成员变量或成员函数的前面加关键字static
- 普通成员变量每个对象各自有各自的,而静态成员变量一共就一份,大家共享,所以没有对象也能访问,想个全局变量似的
- 静态成员函数中,不能访问非静态的东西
成员对象和封闭类
- 成员对象:类的成员变量是另一个类的对象
- 封闭类:包含成员对象的类
封闭类里包含了其他的类的对象,所以生成封闭类对象时,应该也要知道里边的对象该怎么初始化
在定义封闭类的构造函数时,添加初始化列表:
CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y){...}
调用顺序:
- 对象生成时
- 成员对象的构造函数
- 封闭类的构造函数
- 成员对象的构造函数调用顺序
- 和成员对象在类中的说明顺序一致
- 与初始化列表无关
- 对象消亡时
- 封闭类的析构函数
- 成员对象的析构函数
- 析构与构造调用顺序相反
友元
-
友元函数:在一个类里边,用friend加到前面来声明一个函数(这个函数可以是另一个类的成员函数),这个函数就是该类的友元函数,可以访问该类的私有成员
class A { friend void B::function(); };
-
友元类:声明一个类是本类的友元,那么那个类的成员函数都可以访问本类的私有成员
class A { friend class B; };
this指针
指向成员函数当前所作用的对象
静态成员函数不能使用this指针,因为静态成员函数并不具体作用于某个对象
常量对象、常量成员函数和常引用
-
常量对象:不希望这个对象被修改,在前面加const关键字
const Demo Obj;
-
常量成员函数:在成员函数说明后面加const关键字,它不能修改其所作用的对象,所以它不能修改成员变量的值。也不能调用同类的非常量成员函数(静态成员变量和静态成员函数除外,因为静态的东西不算某个对象所自有)
(有const的和没const的函数,其他一样,算重载)void Sample::Get Value() const
-
常引用:不能通过常引用修改其引用的变量,在前面加const关键字
void Printf Obj( const Sample & o)
函数的参数需要传递对象的时候,直接传递对象需要调用复制构造函数,效率低,所有可以用引用(或指针),但是这样有危险(函数有可能一不小心对引用的对象进行修改),所以呢就加一个const,这样的话一旦想修改就会编译错误了
Week 4 运算符重载
运算符重载的基本概念
- C++预定义了一些运算符,用于基本数据类型的运算
- 对运算符进行重载,让自定义的类也能使用运算符,比较方便,运算符本质上就是函数
- 运算符可以被重载为普通函数,也能重载为类的成员函数
-
重载为普通函数
Complex operator+ (const Complex & a, const Complex & b) { return Complex( a.real+b.real, a.imaginary+b.imaginary); }
这时,仅仅是对 '+' 这个运算符的功能进行了扩充,参数个数还是本身的运算符目数
-
重载为成员函数
Complex Complex::operator+(const Complex & operand2) { return Complex( real + operand2.real, imaginary + operand2.imaginary ); } // 在类里边还得声明,或者直接在类中定义,这样就是类的成员函数了
这时,参数个数为本身的运算符目数减一,因为现在执行这个成员函数的对象本身不需要放在参数里面了
-
赋值运算符的重载
- 赋值运算符“=”只能重载为成员函数
- 对“=”进行重载,就能自定义把一个类型的对象直接赋值给另一个类型的对象
比如:
将一个字符串复制给另一个字符串,直接使用“=”,算是浅复制(指向字符串的指针之间的复制),我们可以重载“=”实现深复制(指针所指向的字符串内容间的复制)
返回值可以是 String &,这样会比较方便
运算符重载为友元函数
有时,成员函数不能满足使用要求,普通函数又不能访问类的私有成员,这时可以重载为友元
流插入运算符和流提取运算符的重载
cout << 5 << "abc"
这种写法是因为在iostream里对“<<”进行了重载
自增自减运算符的重载
自增自减本来都是一元运算符,重载时为了区分吧,然后前置运算符作为一元运算符重载T operator++();
,后置运算符作为二元运算符重载T operator++(int);
,仅仅只是表面上多一个参数。
-
int可作为一个类型强制转换运算符被重载:
operator int(){ return n;}
,然后就能这些写了:(int) s;
等效于s.int()
运算符重载的注意事项
- 不允许定义新的运算符
- 不改变运算符的优先级
- 最好符合日常习惯
- 以下运算符不能被重载: . * :: ?: sizeof
- 运算符(),[],->, = ,必须声明为类的成员函数
Week 5 继承与派生
继承与派生
class 派生类名: public 基类名{...};
派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。
继承关系与复合关系
- 继承:“是”关系
B是基类A的派生类,则“一个B对象也是一个A对象” - 复合:“有”关系
一个对象中的有一个成员变量是另一个类的对象
基类/派生类同名成员与Protected关键字
void derived::access() {
j = 5;//error(派生类没有变量j)
i = 5; //引用的是派生类的 i
base::i = 5; //引用的是基类的 i
func(); //派生类的
base::func(); //基类的
}
派生类的成员函数可以访问当前对象的基类的保护成员
派生类的构造函数
FlyBug :: FlyBug (int legs, int color, int wings): Bug(legs, color) {
n Wings = wings;
}
- 派生类对象包含基类对象
- 执行派生类构造函数之前, 先执行基类的构造函数(析构函数相反)
- 除了上面的那个显式方式,还可以是隐式方式(派生类的构造函数中, 省略基类构造函数,自动调用基类的默认构造函数)
- 顺序:基类构造函数\(\rightarrow\)成员对象类的构造函数\(\rightarrow\) ... (析构函数相反)
public继承的赋值兼容规则
如果是public(其他不行):
- 派生类对象可以赋值给基类对象
b = d;
- 派生类对象可以初始化基类引用
base &br = d;
- 派生类对象的地址可以赋值给基类指针
base *pb = & d;
Week 6 虚函数与多态
虚函数和多态
-
虚函数
class base { virtual int get() ; }; int base::get() //函数声明时加virtual就行 { }
-
多态的表现形式1:
通过基类指针调用基类与派生类中同名的虚函数时,
若,指针指向的是基类对象,则调用基类的虚函数
若,指向的是派生类的对象,则调用派生类虚函数 -
多态的表现形式2:
用基类引用来实现,和上面的基本一样 -
多态的好处:增强程序的可扩充性
比如,很多的类都继承于同一个基类,它们有一些类似的操作,需要使用这些类的对象们,如果没有多态,那么代码的每一个语句都需要具体到哪个类,这样需要写很多类似的重复性代码,增加新的类时很不方便,而使用多态的话,不需要在类的代码里明确到底是对那个类对象进行操作,全都使用基类指针或引用,实际使用时让这个指针指向谁就操作谁, 这样就方便多了。
多态实现原理
“多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定 —— 这叫“动态联编”。
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的“虚函数表”的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
虚析构函数
如果不是虚的析构函数,那么使用基类指针删除派生类对象时,只调用基类的析构函数,这样派生类对象实际上还没被删掉。所以我们这样:把基类的析构函数声明为virtual(这时派生类的析构函数就自动也是虚函数了),这时使用基类指针删除派生类对象,会首先调用派生类的析构函数,然后调用 基类的析构函数。
- 如果定义了虚函数,最好将析构函数也定义为虚函数
- 不允许有虚的构造函数
纯虚函数和抽象类
- 纯虚函数:没有函数体的虚函数
virtual void Print() = 0;
- 抽象类:包含纯虚函数的类(只能用来派生新类,不能创建对象)
- 成员函数里可以调用纯虚函数
- 构造/析构函数里不能调用纯虚函数
- 实现了所有纯虚函数的派生类才能称为非抽象类
Week 7 文件操作和模板
文件操作
- ifstream,ofstream,fstream 用于文件操作,统称为文件流类
- 基本流程:打开文件(连接,使用方式)\(\rightarrow\)读写文件\(\rightarrow\)关闭文件
ofstream out File(“clients.dat”, ios::out|ios::binary); //打开文件
out 删除原有内容, app 在尾部添加 binary 以二进制格式- 或者
ofstream fout; fout.open( “test.out”, ios::out|ios::binary );
- 判断打开是否成功
if(!fout) { cerr << “File open error!”<<endl; }
- 读指针/写指针/读写指针,指向哪里就在哪进行读写操作。tellp(); seekp(); seekg(); ...
fout.write( (const char *)(&x), sizeof(int) );
fin.read( (char *)(&x), sizeof(int) );
函数模板
-
泛型程序设计,算法实现时不指定具体要操作的数据的类型,包括函数模板、类木板
-
比如,交换两个变量的值,编译器会根据调用的情况生成相应数据类型的函数
template <class T> void Swap(T & x, T & y) { T tmp = x; x = y; y = tmp; }
-
编译器先找普通函数,再去找模板函数,再找实参自动类型转换后能匹配的普通函数,如何还找不到就报错
类模板
-
Pair类模板:
template <class T1, class T2> class Pair{ public: T1 key; //关键字 T2 value; //值 Pair(T1 k,T2 v):key(k),value(v) { }; bool operator < (const Pair<T1,T2> & p) const; }; template<class T1,class T2> bool Pair<T1,T2>::operator<( const Pair<T1, T2> & p) const { return key < p.key; }
-
Pair类模板的使用
int main() { Pair<string, int> student("Tom",19); //实例化出一个类 Pair<string, int> cout << student.key << " " << student.value; return 0; }
-
类模板的实例化得到的类叫模板类
-
类模板的参数声明中可以包括非类型参数:
template <class T, int elements Number>
非类型参数: 用来说明类模板中的属性 -
类模板与继承:
类模板派生出类模板,模板类派生出类模板,普通类派生出类模板,模板类派生出普通类
string类
- 初始化
string s1("Hello");
string s2(8, 'x');
string month = "March";
- 可以将字符赋值给string对象
s = 'n'
- 长度
s.length()
- 流读取运算符'cin >> string Object;'
getline(cin, s);
- string的赋值和连接
- 赋值
s2 = s1;
- 复制
s3.assign(s1);
- 部分复制
s3.assign(s1, 1, 3);
- 单个字符复制
s2[5] = s1[3] = 'a';
- s1.at(i),会做范围检查
- 用+连接
s1 += s2;
s1.append(s2);
- 赋值
- 比较string
- 关系运算符== , >, >=, <, <=, !=
int f1 = s1.compare(s2);
- 子串
s2 = s1.substr(4,5);
- 交换
s1.swap(s2);
- capacity(); maximum_size(); length(); size(); empty(); resize()
- find(); rfind(); find_first_of(); find_last_of(); find_first_not_of(); find_last_not_of();
- erase(); replace(); insert();
- c_str(); data(); copy();
输入输出
- 类:istream, ostream, ifstream, ofstream, iostream, fstream.
- 标准流对象:cin, cout, cerr, clog
- 输出重定向:
freopen("test.txt","w",stdout); //将标准输出重定向到 test.txt文件
- 输入重定向:
freopen(“t.txt”,“r”,stdin); //cin被改为从 t.txt中读取数据
- 判断输入流结束:
while(cin>>x){...}
- 键盘 Ctrl+Z 代表输入流结束
- getline:
istream & getline(char * buf, int buf Size);
istream & getline(char * buf, int buf Size,char delim);
if(!cin.getline(…))
- bool eof(); 判断输入流是否结束
- int peek(); 返回下一个字符,但不从流中去掉
- istream & putback(char c); 将字符ch放回输入流
- istream & ignore( int n Count = 1, int delim = EOF ); 从流中删掉最多n Count个字符,遇到EOF时结束
Week 8 标准模板库 STL -1
概述
- 泛型程序设计:使用模板的程序设计法。
- 容器:类似于类模板
- 顺序容器(元素没有按值进行排序):vector(动态数组),deque(双向队列),list(双向链表)
- 关联容器(元素是排序的,平衡二叉树):set(集合), multiset(允许相同元素),map(每个元素包含first和second),multimap(允许相同的first)
- 容器适配器:stack(栈),queue(队列),priority_queue(优先级队列)
- 迭代器:类似于指针
vector::const_iterator i; //常量迭代器
for( i = v.begin();i != v.end();++i )
cout << * i << ",";
reverse_iterator(反向迭代器),iterator(非常量迭代器)- 容器上的迭代器类别
- 随机访问:vector,deque
- 双向:list,set/multiset,map/multimap
- 不支持迭代器:stack,queue,priority_queue
- 容器上的迭代器类别
- 算法: 类似于函数模板
- find():
p = find(v.begin(),v.end(),3);
返回一个迭代器,指向找到的元素,或者last
- find():
- 顺序容器和关联容器都有的成员函数:
begin,end,rbegin,rend,(返回迭代器),erase,clear - 顺序容器常用成员函数:
front,back,(返回元素引用),push_back,pop_back,erase - ”x和y相等“有时等价于”x==y为真“,有时等价于”x小于y和y小于x同时为假“
vector
可变长动态数组,#include <vector>
,所有STL算法都支持
-
初始化:
vector();
vector(int n);
vector(int n, const T & val);
vector(iterator first, iterator last);
-
常用成员函数
成员函数 | 作用 |
---|---|
void pop_back(); |
删除容器末尾的元素 |
void push_back(const T & val); |
将val添加到容器末尾 |
int size(); |
返回容器中元素的个数 |
T & font(); |
返回容器中第一个元素的引用 |
T & back(); |
返回容器中最后一个元素的引用 |
list和deque
- list容器还支持:push_front,pop_front,sort,remove,unique,merge,reverse,splice
- list容器的sort函数(由于list不支持完全随机访问,不能用STL的sort函数)
list<T> classname
classname.sort(compare);
classname.sort();
- deque除了所有适用于vector的操作,还有push_front和pop_front
函数对象
若一个类重载了运算符“()”,则该类的对象就成为函数对象
以下模板可以用来生成函数对象:equal_tp,greater,less ...头文件
lst.sort(greater<int>()); //greater<int>()是个对象
Week 9 标准模板库 STL -2
set和multiset,map和multimap
- set,multiset,map,multimap还支持:find,lower_bouond,upper_bound,equal_range,count,insert
- pair模板:pair模板类的对象包含first和second两个成员变量,map/multimap放着的就是这些
- multiset:
template<class Key, class Pred = less<Key>, class A = allocator<Key> >
class multiset { …… };
- set:
template<class Key, class Pred = less<Key>, class A = allocator<Key> >
class set { … }
- multimap:
template<class Key, class T, class Pred = less<Key>,class A = allocator<T> >
class multimap { …...pedef pair<const Key, T> value_type; …….}; //Key 代表关键字的类型
- map
template<class Key, class T, class Pred = less<Key>,class A = allocator<T> >
class map { ….typedef pair<const Key, T> value_type; …….};//关键字(first)各不相同
- map的[]成员函数,e.g. pairs[key]
容器适配器
用某种顺序容器来实现,让已有的顺序容器以栈/队列的方式工作,stack,queue,priority_queue
- 三个成员函数
- push:添加元素
- top:返回栈顶部或队头元素的引用
- pop:删除一个元素
- STL的各种排序,查找,变序等算法都不适合容器适配器
STL算法
STL算法大致可以分为一下七类:
- 不变序列算法:
不修改,适用于顺序容器和关联容器,O(n)
min,max,min_element,max_element,for_each,count,count_if,find,find_if,find_end,find_first_of,adjacent_find,search,search_n,equal,mismatch,lexicographical_cpmpare - 変值算法:
会修改,修改的区间不可以是属于关联容器的
for_each,copy,copy_backward,transform,swap_ranges,fill,fill_n,generate,generate_n,replace,replace_if,replace_copy,replace_copy_if - 删除算法:
删除某些元素,O(n),删除是指:将删掉的元素看作空位,然后留下的元素前移补上,最后没被补上的空位维持其原来的值不变
remove,remove_if,remove_copy,remove_copy_if,unique,unique_copy - 变序算法:
改变顺序不改变值,不适用于关联容器,O(n)
reverse,reverse_copy,rotate,rotate_copy,next_permulation,prev_permulation,random_shuffle,partition - 排序算法:
一般是O(nlog(n)),需要随机访问迭代器,不适用于关联容器和list
sort,stable_sort,partial_sort,partial_sort_copy,nth_element,make_heap,push_heap,pop_heap,sort_heap - 有序区间算法
要求区间已经排号序,需要随机访问迭代器,不能用于关联容器和list
binary_search,includes,lower_bound,upper_bound,equal_range,merge,set_union,set_intersection,set_difference,set_symmetric_difference,inplace_merge - 数值算法...
-
大多数重载的算法都有两个版本,比如:
iterator min_element(iterator first, iterator last);
iterator min_element(iterator first, iterator last, Pred op);//表达式op(x, y)的返回值来判断x与y的大小
-
bitset:
template<size_t N>
class bitset{ ...... };
比如- bitset<40> bst;一个由40位组成的对象
作者:[rubbninja](http://www.cnblogs.com/rubbninja/) 出处:[http://www.cnblogs.com/rubbninja/](http://www.cnblogs.com/rubbninja/) 关于作者:目前主要研究领域为机器学习与无线定位技术,欢迎讨论与指正!