侯捷C++(STL和泛型编程)
Remember Understand Practice Use Understand Master
一、STL和泛型编程
C++标准库
C++标准模板库
STL六大部件:
容器(Containers)
分配器(Allocators)
算法(Algorithms)
迭代器(Iterators)
适配器(Adapters)
仿函式(Functors)
#include <iostream> using namespace std; int main() { for(int i: {2,3,5,7,9,13,17,19}) { cout<<i<<' '; } return 0; }
1.容器
Sequence containers:
Array
Vector
heap
priority——queue
优先队列是一种容器适配器,采用了堆这样的数据结构,保证了第一个元素总是整个优先队列中最大的(或最小的)元素。优先队列默认使用vector作为底层存储数据的容器
Deque
Stack
Queue
List
Slist
Forward-List
Associative containers:
Set/Multiset
Map/Multimap
Unordered Set/Multiset
Unordered Map/Multimap
2.分配器
面向对象编程VS泛型编程
Operator Overloading操作符重载
Templates 模板
ClassTemplates 类模板
Function Templates 函数模板
Member Templates 成员模板
检查数据类型
typeid() typeid 用于在编译时获取类型的 Type
3.算法
4.迭代器的分类
二、C++标准11-14
C++ Primer回顾:
1.可以用sizeof测量数据类型的大小,以字节为单位
1.类定义
类成员
一般情况下,数据成员为private
没写访问标号,默认private
构造函数
使用初始化列表初始化数据成员
Person(const string &nm,const string &addr):name(nm),address(addr){ //name = nm; //address = addr; }
成员函数
访问标号实施抽象和封装
public
private
protected
#include <iostream> #include <string> #include <cstring> using namespace std; class Sales_item { public: Sales_item(string &book,unsigned units,double amount): isbn(book),units_sold(units),revenue(amount) { } double avg_price() const { if(units_sold) return revenue / units_sold; else return 0; } bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } void add(const Sales_item &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; } private: string isbn; unsigned units_sold; double revenue; }; class Person{ public: Person(const string &nm,const string &addr):name(nm),address(addr){ //name = nm; //address = addr; } string getName() const { return name; } string getAddress() const { return address; } private: string name; string address; }; int main() { //Person a("bill","street1"); //a.name; //cout<<a.getName(); //a.getAddress(); string s("0-399-82477-1"); Sales_item x(s,2,20.00); Sales_item y(s,6,48.00); if(x.same_isbn(y)) x.add(y); cout<<x.avg_price()<<endl; cout<<y.avg_price(); return 0; }
2.
同一类型的多个数据成员
使用类型别名来简化类
成员函数可被重载 - 定义重载成员函数
显法指定inline成员函数
写在类内部的函数都是内联函数。
函数定义写在外部则不是内联函数,可在定义或声明前增加inline
#include <iostream> #include <string> using namespace std; class Screen { public: typedef string::size_type index; Screen(index ht = 0,index wd = 0):contents(ht*wd,'A'),cursor(0),height(ht),width(wd) { } Screen(index ht,index wd,const string &conts); char get() const; inline char get(string::size_type r,string::size_type c) const; private: string contents; string::size_type cursor; string::size_type height,width; }; inline char Screen::get() const { return contents[cursor]; } char Screen::get(string::size_type r,string::size_type c) const { string::size_type row = r*width; return contents[row+c]; } Screen::Screen(index ht,index wd,const string &conts):contents(conts), cursor(0),height(ht),width(wd) { } int main() { Screen a(10,100); cout<<a.get()<<endl; cout<<a.get(2,8)<<endl; Screen b(3,6,"hello screen class"); cout<<b.get()<<endl; cout<<b.get(0,4)<<endl; cout<<b.get(1,2); return 0; }
3.隐含的this指针
this指向当前的对象
何时使用this指针
返回*this
从const成员函数返回*this
基于const的重载
可变数据成员
4.类作用域
1.使用类的成员
2.作用域与成员定义
3.形参表和函数体处于类作用域中
4.函数返回类型不一定在类作用域中
类作用域中的名字查找
1.类成员声明的名字查找
2.类成员定义中的名字查找
3.类成员遵循常规的块作用域名字查找
4.函数作用域之后,在类作用域中查找
5.类作用域之后,在外围作用域中查找
5.构造函数
作用保证每个对象的数据成员具有合适的初始值
构造函数初始化式(初始化列表)
默认实参与构造函数
默认构造函数
对string进行初始化为空,其他类型没有默认初始化
const成员、引用类型、没有默认构造函数的类类型必须使用初始化列表
隐式类类型转换
类成员的显示初始化
#include<iostream> #include <string> using namespace std; class Person { public: Person(const string &nm,int a):name(nm),age(a) { } public: string name; int age; }; class Dog { public: Dog() { this->legs = 4; } private: string name; int legs; }; class Sales_item { public: Sales_item(const string &book,int units,double price):isbn(book),units_sold(units),revenue(units*price) { } //默认的实参,不给参数按照默认的 Sales_item(const string &book=""):isbn(book),units_sold(0),revenue(0.0){} Sales_item(istream &is) {is >> *this;} //Sales_item():units_sold(0),revenue(0.0){} Sales_item(int units,double price) { this->units_sold = units; this->revenue = units*price; } friend istream& operator >> (istream&,Sales_item&); private: string isbn; unsigned units_sold; double revenue; }; inline istream& operator>>(istream& in,Sales_item& s) { double price; in>>s.isbn>>s.units_sold>>price; if(in) s.revenue = s.units_sold * price; else s = Sales_item(); return in; } class Cat{ public: Cat():age(0){} string getName(){ return this->name; } int getAge() { return this->age; } private: string name; int age; }; int main() { Person a("zhangfei",22); cout<<a.name<<endl; cout<<a.age; Sales_item item1; Sales_item item2("0-201-82470-1"); Sales_item *p = new Sales_item(); delete [] p; return 0; }
6.友元-友元函数、友元类
友元关系
三种友元
1.普通函数(非成员函数)
2.类
3.类的成员函数
#include <iostream> #include <string> using namespace std; class Screen; class Dog { public: int foo(Screen& screen); int koo(Screen& screen); }; class Screen { public: typedef string::size_type index; Screen(int ht=0,int wd=0):contents(ht*wd,' '),cursor(0),height(ht),width(wd) { } int area() const { return height*width; } friend int calcArea(Screen &); friend class Window_Mgr; //friend class Dog; friend int Dog::foo(Screen&); private: string contents; index cursor; int height,width; }; class Window_Mgr { public: void relocate(int r,int c,Screen& s) { s.height += r; s.width += c; } }; // 这个函数不是类的成员函数 int calcArea(Screen &screen) { return screen.height * screen.width; } int Dog::foo(Screen& screen) { return screen.height * screen.width; } int main() { Screen a(60,100); cout<<a.area()<<endl; Window_Mgr wm; wm.relocate(20,100,a); cout<<calcArea(a)<<endl; Dog dog; cout<<dog.foo(a)<<endl; // cout<<dog.koo(a)<<endl; cout<<"OK"<<endl; return 0; }
7.static类成员
使用类的static成员的优点
公用
定义static成员
使用类的static成员
static成员函数
static数据成员
#include <iostream> #include <string> using namespace std; class Account { public: Account(string name,double money): owner(name),amount(money) { } double getAmount() const { return this->amount; } void applyint() { amount+=amount*interestRate; } void deposit(double money) { this->amount += money; } static double rate() { return interestRate; } static void rate(double newRate){interestRate = newRate;} private: string owner; double amount; static double interestRate; static const int period = 30;//例外 }; double Account::interestRate = 0.015; int main() { Account::rate(0.026); Account a("zhangsan",1000); a.deposit(500); Account b("lisi",2000); b.deposit(600); cout<<a.getAmount()<<endl; cout<<b.getAmount()<<endl; cout<<a.rate()<<endl; a.rate(0.018); cout<<b.rate()<<endl; Account::rate(0.02); a.applyint(); b.applyint(); cout<<a.getAmount()<<endl; cout<<b.getAmount()<<endl; return 0; }
8.复制构造函数和赋值操作符
复制构造函数的适用情况
1.对象的定义形式 - 复制初始化
2.形参与返回值
3.初始化容器元素
4.构造函数与数组元素
赋值操作符
1.重载赋值操作符
2.复制和赋值常一起使用
合成的复制构造函数和赋值操作符
定义自己的复制构造函数和赋值操作符
#include <iostream> #include <string> #include <vector> using namespace std; class Sales_item { public: //普通构造函数 Sales_item():units_sold(0),revenue(0.0){ cout<<"This 1"<<endl; } Sales_item(const string &book):isbn(book),units_sold(0),revenue(0.0) { cout<<"This 2"<<endl; } //复制构造函数 Sales_item(const Sales_item &orig):isbn(orig.isbn), units_sold(orig.units_sold),revenue(orig.revenue) { cout<<"DYL"<<endl; } //赋值操作符 当类中动态分配内存时,必须重载赋值操作符 Sales_item& operator=(const Sales_item &rhs) { cout<<"DYLLLLLL"<<endl; isbn = rhs.isbn; units_sold = rhs.units_sold; revenue = rhs.revenue; return *this; } private: string isbn; unsigned units_sold; double revenue; }; Sales_item foo(Sales_item &item) { Sales_item temp; temp = item; return temp; } class noName { public: noName():pstring(new string), i(0),d(0.0) { } noName(const noName& other):pstring(new string(*(other.pstring))), i(other.i),d(other.d) { } noName& operator=(const noName &rhs) { pstring = new string; *pstring = *(rhs.pstring); i=rhs.i; d=rhs.d; return *this; } private: string *pstring; int i; double d; }; int main() { noName x,y; noName z(x); x = y; Sales_item a; Sales_item b("0-201-78345-X"); Sales_item c(b); a = b; Sales_item item = string("9-999-99999-9"); cout<<"try foo()"<<endl; Sales_item ret; ret = foo(item); cout<<"try vector"<<endl; vector<Sales_item> vec(5); cout<<"try array"<<endl; Sales_item primer_eds[] = { string("0-201-16487-6"), string("0-201-16487-7"), string("0-201-16487-7"), Sales_item() }; return 0; }
9.析构函数
析造函数:获取资源
析构函数:释放资源
合成的析构函数
三法则:如果写了析构函数,一定要写复制构造函数和赋值操作符重载。
#include <iostream> using namespace std; class NoName { public: NoName():pstring(new string),i(0),d(0.0) { cout<<"构造函数被调用了"<<endl; } ~NoName(); NoName(const NoName& other); NoName& operator=(const NoName &rhs); private: string *pstring; int i; double d; }; NoName::~NoName() { //关闭文件 //关闭数据库链接 //回收动态分配的内存 cout<<"析构函数被调用了"<<endl; delete pstring; } NoName::NoName(const NoName& other) { pstring = new string(); *pstring = *(other.pstring); i = other.i; d = other.d; } NoName& NoName::operator=(const NoName &rhs) { pstring = new string; *pstring = *(rhs.pstring); i = rhs.i; d = rhs.d; return *this; } int main() { NoName a; NoName *p = new NoName; delete p; return 0; }
10.深复制、浅复制
复制构造函数/拷贝构造函数
浅复制/浅拷贝/位拷贝
深复制/深拷贝
#include <iostream> #include <cstring> using namespace std; class CDemo { //默认的复制构造函数是浅复制 public: CDemo(int pa,char *cstr) { this->a = pa; this->str = new char[104]; strcpy(this->str,cstr); } CDemo(CDemo &obj) { this->a = obj.a; this->str = new char[1024]; if(str!=0) strcpy(this->str,obj.str); } ~CDemo() { delete str; } int a; char *str; }; int main() { CDemo A(10,"hello"); CDemo B = A; cout<<A.a<<" "<<A.str<<endl; cout<<B.a<<" "<<B.str<<endl; B.a = 8; B.str[0]='k'; cout<<A.a<<" "<<A.str<<endl; cout<<B.a<<" "<<B.str<<endl; return 0; }
11.管理指针成员
三种方法
1.常规指针类(浅复制)
严重缺点:野指针(悬垂指针)
class AHasPtr { public: AHasPtr(int *p,int i):ptr(p),val(i) { } int *get_ptr() const {return ptr;} int get_int() const {return val;} void set_ptr(int *p) {ptr=p;} void set_int(int i){val=i;} int get_ptr_val() const {return *ptr;} void set_ptr_val(int val) {*ptr = val;} private: int val; int *ptr; };
2.智能指针类(计数类)
避免野指针
class CHasPtr { public: CHasPtr(const int &p,int i):ptr(new int(p)),val(i) { } CHasPtr(const CHasPtr &orig):ptr(new int(*orig.ptr)), val(orig.val){} CHasPtr& operator=(const CHasPtr&); ~CHasPtr() { delete ptr; } int *get_ptr() const {return ptr;} int get_int() const {return val;} void set_ptr(int *p) {ptr=p;} void set_int(int i){val=i;} int get_ptr_val() const {return *ptr;} void set_ptr_val(int val) {*ptr = val;} private: int val; int *ptr; }; CHasPtr& CHasPtr::operator=(const CHasPtr &rhs) { *ptr = *rhs.ptr; val = rhs.val; return *this; }
3.值型类(深复制)
class U_Ptr { friend class BHasPtr; private: int *ip; size_t use;//计数 U_Ptr(int *p):ip(p),use(1){} ~U_Ptr(){delete ip;} }; class BHasPtr { public: BHasPtr(int *p,int i):ptr(new U_Ptr(p)),val(i) { } BHasPtr(const BHasPtr& orig):ptr(orig.ptr),val(orig.val){ ++ptr->use; } BHasPtr& operator=(const BHasPtr&); ~BHasPtr() { //此处似乎有问题 //if(--ptr->use == 0) delete ptr; } int *get_ptr() const {return ptr->ip;} int get_int() const {return val;} void set_ptr(int *p) {ptr->ip=p;} void set_int(int i){val=i;} int get_ptr_val() const {return *ptr->ip;} void set_ptr_val(int val) {*ptr->ip = val;} private: int val; U_Ptr *ptr; }; BHasPtr& BHasPtr::operator=(const BHasPtr &rhs) { ++rhs.ptr->use; if(--ptr->use == 0) delete ptr; ptr = rhs.ptr; val = rhs.val; return *this; }
#include <iostream> #include "plain-ptr.h" #include "value-ptr.h" #include "smart-ptr.h" using namespace std; void test_AHasPtr() { int i=42; AHasPtr p1(&i,42); AHasPtr p2 = p1; cout<<p2.get_ptr_val()<<endl; p1.set_ptr_val(0); cout<<p2.get_ptr_val()<<endl; int *ip = new int(42); AHasPtr ptr(ip,10); cout<<ptr.get_ptr_val()<<endl; delete ip; cout<<ptr.get_ptr_val()<<endl; } void test_CHasPtr() { int obj = 0; CHasPtr ptr1(obj,42); CHasPtr ptr2(ptr1); cout<<ptr1.get_ptr_val()<<endl; cout<<ptr1.get_int()<<endl; cout<<ptr2.get_ptr_val()<<endl; cout<<ptr2.get_int()<<endl; ptr2.set_ptr_val(10); cout<<ptr1.get_ptr_val()<<endl; cout<<ptr1.get_int()<<endl; cout<<ptr2.get_ptr_val()<<endl; cout<<ptr2.get_int()<<endl; } void test_BhasPtr() { int obj=0; BHasPtr ptr1(&obj,42); BHasPtr ptr2(ptr1); cout<<ptr1.get_ptr_val()<<","<<ptr1.get_int()<<endl; cout<<ptr2.get_ptr_val()<<","<<ptr2.get_int()<<endl; ptr2.set_ptr_val(39); ptr1.set_int(23); cout<<ptr1.get_ptr_val()<<","<<ptr1.get_int()<<endl; cout<<ptr2.get_ptr_val()<<","<<ptr2.get_int()<<endl; } int main() { cout<<"常规指针"<<endl; test_AHasPtr(); cout<<"值型类"<<endl; test_CHasPtr(); cout<<"智能指针"<<endl; test_BhasPtr(); return 0; }
12.重载操作符的定义
不能重载的操作符(四个)
:: .*
. ?:
重载操作符的注意事项
操作符重载
输出操作符 >> 重载
非成员函数 ->友元函数
少做格式化
输入操作符 >>重载
处理输入操作的错误
#include <iostream> #include <string> using namespace std; class Dog { }; class Sales_item { friend ostream& operator<<(ostream& out,const Sales_item& s); friend istream& operator>>(istream& in,Sales_item& s); public: Sales_item(const string &book,unsigned units,double price): isbn(book),units_sold(units),revenue(price*units) { } Sales_item():units_sold(0),revenue(0.0) { } private: string isbn; unsigned units_sold; double revenue; }; ostream& operator<<(ostream& out,const Sales_item& s) { out<<s.isbn<<"\t"<<s.units_sold<<"\t"<<s.revenue; return out; } istream& operator>>(istream& in,Sales_item& s) { double price; in>>s.isbn>>s.units_sold>>price; s.revenue = s.units_sold*price; if(in) s.revenue = s.units_sold*price; else s = Sales_item(); return in; } int main() { //Dog a; Sales_item item("0-201-78345-X",2,25.00); cout<<"hello"<<endl; cout<<item<<endl; cin>>item; cout<<item<<endl; cin>>item; cout<<item<<endl; return 0; }
为了与内置操作符保持一致,算术操作符通常产生一个新值。
一般应使用复合赋值实现算术操作符。例如:用+=来实现+
#include <iostream> #include <string> using namespace std; class Dog { }; class Cat { }; class Sales_item { public: Sales_item(const string&book,const unsigned units,const double amount): isbn(book),units_sold(units),revenue(amount) { } Sales_item& operator+=(const Sales_item&); Sales_item& operator-=(const Sales_item&); friend ostream& operator<<(ostream&,const Sales_item&); private: string isbn; unsigned units_sold; double revenue; }; Sales_item operator+(const Sales_item&,const Sales_item&); Sales_item operator-(const Sales_item&,const Sales_item&); Sales_item& Sales_item::operator+=(const Sales_item &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_item& Sales_item::operator-=(const Sales_item& rhs) { this->units_sold -= rhs.units_sold; this->revenue -= rhs.revenue; return *this; } Sales_item operator+(const Sales_item& lhs,const Sales_item& rhs) { Sales_item ret(lhs); ret += rhs; return ret; } Sales_item operator-(const Sales_item& lhs,const Sales_item& rhs) { Sales_item ret(lhs); ret -= rhs; return ret; } ostream& operator<<(ostream& out, const Sales_item& s) { out<<s.isbn<<"\t"<<s.units_sold<<"\t"<<s.revenue; return out; } int main() { //Dog dog; //Cat cat; Sales_item item1("0-201-12345-X",10,120.0); Sales_item item2("0-201-12345-X",20,200.0); Sales_item result = item1 + item2; cout<<result<<endl; Sales_item item3("0-201-12345-X",5,70.00); result += item3; cout<<result<<endl; result -= item2; cout<<result<<endl; return 0; }
重载关系操作符(大于小于等于)
13.函数对象
重载函数调用操作符
函数对象:定义了调用操作符的类,其对象称为“函数对象”
一元函数对象
一元谓词
二元函数对象
二元谓词
#include <iostream> #include <string> #include <vector> #include <list> #include <algorithm> using namespace std; struct absInt{ int operator()(int val) { return val<0?-val:val; } }; template<typename elementType> void FuncDisplayElement(const elementType& element) { cout<<element<<' '; } template<typename elementType> struct DisplayElement { //存储状态 int m_nCount; DisplayElement() { m_nCount = 0; } void operator() (const elementType& element) { ++m_nCount; cout<<element<<' '; } }; int main() { int i = -42; absInt absObj; unsigned int ui = absObj(i); cout<<ui<<endl; vector<int> a; for(int n=0;n<10;++n) a.push_back(n); list<char> b; for(char c='a';c<'k';++c) b.push_back(c); //STL算法 DisplayElement<int> mResult; mResult = for_each(a.begin(),a.end(),mResult); cout<<endl; cout<<"数量:"<<mResult.m_nCount<<endl; for_each(b.begin(),b.end(),DisplayElement<char>()); cout<<endl; cout<<"Hello 函数对象"<<endl; return 0; }
一元函数谓词
#include <iostream> #include <vector> #include <algorithm> using namespace std; template<typename numberType> struct IsMutiple { numberType m_Divisor; IsMutiple(const numberType& divisor) { m_Divisor = divisor; } bool operator() (const numberType& element) const { return ((element % m_Divisor)==0); } }; int main() { vector<int> vecIntegers; for(int i=33;i<=100;++i) vecIntegers.push_back(i); IsMutiple<int> a(4); vector<int>::iterator iElement; iElement = find_if(vecIntegers.begin(),vecIntegers.end(),IsMutiple<int>(4)); if(iElement != vecIntegers.end()) { cout<<"第一个4的整数倍的数是: "<<*iElement<<endl; } return 0; }
二元函数谓词
#include <iostream> #include <vector> #include <algorithm> using namespace std; template<typename elementType> class CMultiply { public: elementType operator() (const elementType& elem1, const elementType& elem2) { return elem1*elem2; } }; int main() { vector<int> a,b; for(int i=0;i<10;++i) a.push_back(i); for(int j=100;j<110;++j) b.push_back(j); vector<int> vecResult; vecResult.resize(10); transform(a.begin(),a.end(),b.begin(),vecResult.begin(),CMultiply<int>()); for(size_t nIndex = 0;nIndex<vecResult.size();++nIndex) cout<<vecResult[nIndex]<<' '; cout<<endl; return 0; }
#include <iostream> #include <set> #include <string> #include <algorithm> using namespace std; class CCompareStringNoCase { public: bool operator()(const string& str1,const string& str2) const { string str1LowerCase; str1LowerCase.resize(str1.size()); transform(str1.begin(),str1.end(),str1LowerCase.begin(),::tolower); string str2LowerCase; str2LowerCase.resize(str2.size()); transform(str2.begin(),str2.end(),str2LowerCase.begin(),::tolower); return (str1LowerCase<str2LowerCase); } }; int main() { set<string,CCompareStringNoCase> name; name.insert("Tina"); name.insert("jim"); name.insert("Jack"); name.insert("Sam"); name.insert("hello"); set<string,CCompareStringNoCase>::iterator iNameFound = name.find("Jim"); if(iNameFound != name.end()) { cout<<"找到了"<<*iNameFound<<endl; } else { cout<<"没找到"<<endl; } return 0; }
三、C++内存管理
四、C++面向对象高级开发