C++程序设计——知识点总结

时间:2023-03-09 01:23:38
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

  • 编译器会自动生成复制构造函数用于复制
  • 若自定义了,则默认的复制构造函数不存在
  • 复制构造函数起作用的三种情况:
    1. 用一个对象初始化同类的另一个对象
    2. 一个包含类A为参数的函数被调用时
    3. 函数的返回值是对象,函数返回时

类型转换构造函数

和类型转换函数不是一个名词的样子,这个类型转换构造函数,其实就是一个构造函数而已,但是像具有类型转换的功能,把其他的东西转换为本类,比如:

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){...}

调用顺序:

  • 对象生成时
    1. 成员对象的构造函数
    2. 封闭类的构造函数
  • 成员对象的构造函数调用顺序
    • 和成员对象在类中的说明顺序一致
    • 与初始化列表无关
  • 对象消亡时
    1. 封闭类的析构函数
    2. 成员对象的析构函数
  • 析构与构造调用顺序相反

友元

  • 友元函数:在一个类里边,用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(其他不行):

  1. 派生类对象可以赋值给基类对象b = d;
  2. 派生类对象可以初始化基类引用 base &br = d;
  3. 派生类对象的地址可以赋值给基类指针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


概述

  • 泛型程序设计:使用模板的程序设计法。
  • 容器:类似于类模板
    1. 顺序容器(元素没有按值进行排序):vector(动态数组),deque(双向队列),list(双向链表)
    2. 关联容器(元素是排序的,平衡二叉树):set(集合), multiset(允许相同元素),map(每个元素包含first和second),multimap(允许相同的first)
    3. 容器适配器: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
  • 顺序容器和关联容器都有的成员函数:

    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算法大致可以分为一下七类:

  1. 不变序列算法:

    不修改,适用于顺序容器和关联容器,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
  2. 変值算法:

    会修改,修改的区间不可以是属于关联容器的

    for_each,copy,copy_backward,transform,swap_ranges,fill,fill_n,generate,generate_n,replace,replace_if,replace_copy,replace_copy_if
  3. 删除算法:

    删除某些元素,O(n),删除是指:将删掉的元素看作空位,然后留下的元素前移补上,最后没被补上的空位维持其原来的值不变

    remove,remove_if,remove_copy,remove_copy_if,unique,unique_copy
  4. 变序算法:

    改变顺序不改变值,不适用于关联容器,O(n)

    reverse,reverse_copy,rotate,rotate_copy,next_permulation,prev_permulation,random_shuffle,partition
  5. 排序算法:

    一般是O(nlog(n)),需要随机访问迭代器,不适用于关联容器和list

    sort,stable_sort,partial_sort,partial_sort_copy,nth_element,make_heap,push_heap,pop_heap,sort_heap
  6. 有序区间算法

    要求区间已经排号序,需要随机访问迭代器,不能用于关联容器和list

    binary_search,includes,lower_bound,upper_bound,equal_range,merge,set_union,set_intersection,set_difference,set_symmetric_difference,inplace_merge
  7. 数值算法...
  • 大多数重载的算法都有两个版本,比如:

    • 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/)
关于作者:目前主要研究领域为机器学习与无线定位技术,欢迎讨论与指正!