C++程序设计
一、从 C 到 C++
1.1、C++简介
C语言是面向过程、C++是向对象的编程语言
C++是C的升级、面向对象
1.2、C++新增特性
1.2.1、相对于C/ C++ 有更为严格的类型检查
eg:
int *p;
char *q;
p = q; //c 语言警告, 但是c++是错误
1.2.2、新增变量引用
eg:
int a = 10;
int &b = a; //c语言直接报错,c++是为引用(给a 取别名 为 b)
printf(“%d–%d–%p–%p\n”, a,b,&a, &b);
不能同时引用多个变量。
1.2.3、支持面向对象
类和对象、继承、多态、虚函数及RTTI(运行时类型识别)
1.2.4、新增泛型编程
支持模板(template),标准模板库(STL)
1.2.5、支持异常处理
标准异常、自定义异常
1.2.6、支持函数/运算符重载
1.2.7、支持名字空间
用于管理函数名、变量名及类
1.3、OOP简介
1.3.1、面向对象编程( OOP )
OOP是程序设计工程化的一种方法,软件架构的一种思想。
OOP基本原则是程序是由单个能够起到子程序作用的单元或对象组合而成,以达到软件工程的三个主要目标:重用性、灵活性和扩展性。
1.3.2、重用性
代码被重复使用,以减少代码量,就是重用性的重要指标。
1.3.3、灵活性
软件系统由很多可以复用的构件随意重构,以达到实现不同的功能,非常灵活。
1.3.3、扩展性
软件系统可以容易地新增需求,基本构件也可以轻松的扩展功能。
1.3.4、面向过程设计:(c语言面向过程)
程序=数据结构+算法。
1.3.5、面向对象设计:(c++面向对象)
对象=数据结构+算法
程序=(对象+对象+….)+对象间通讯机制。
1.3.6、面向对象编程主要涉及概念如下
类( class )、对象( Object )、数据抽象、继承( inherit )、动态绑定(动态联编)、静态绑定(静态联编)、数据封装( encapsulation )、多态性( polymorphism)、消息传递。
1.4、泛型编程( Generic Programming)
简介:目的是为了实现C++的STL(标准模板库)。
1.5、编译C++程序
1.5.1、C/C++编译步骤 ★★★
(1)、c语言:以.h .c 分别为头文件和源文件
4个步骤: hello.c
预处理:(展开头文件里面的宏和结构体等)gcc -E hello.c -o hello.i
编译:(检查语法错误,及删除预处理后文件内多余的文件)gcc -S hello.i -o hello.s
汇编:(转化为二进制文件)gcc -c hello.s -o hello.o
链接:(生成可执行程序)gcc hello.o -o hello
(2)、c++: 以GNU平台:.cpp 为后缀。 头文件无后缀
4个步骤: hello.cpp
预处理: g++ -E hello.cpp -o hello.i
编译: g++ -S hello.i -o hello.s
汇编: g++ -c hello.s -o hello.o
链接: g++ hello.o -o hello
1.5.2、C++程序的基本结构
1.5.3、源文件的扩展名
1.5.4、引用头文件命名
1.6、C 和 C++兼容及差异
1.6.1、C++对C的“增强”,主要表现在两个方面
在原来面向过程的机制基础上,对C语言的功能做了不少扩充。
增加了面向对象的机制。
1.6.2、常变量
在变量基础上加const 限定:存储单元值不允许被修改。该变量称之为 只读变量(read-only-variable);
1.6.3、强制类型转换
(1)、c语言里面转换一般形式:
(类型名)(表达式)
eg:
int i;
char ch;
ch = (char)i; //将i 强制转换为char 类型
struct sockaddr_in saddr;
(struct sockaddr *)&saddr; //强转
(2)、c++一般转换形式:
类型名 (表达式)
int i;
char ch;
ch = char(i); //将i 强制转换为char 类型
1.6.4、变量引用 reference
对一个数据可以使用“引用”,这是C++对C的一个重要扩充,引用是一种新的变量类型,它的作用是为一个变量起一个别名。
不能同时引用两个变量。
C中函数之间的参数传递方式有:复制传递方式、地址传递方式:C++增加了函数参数引用。
1.6.5、内联函数
可以在内部定义成员函数,也是内联函数。
优点:提升代码运行效率;缺点:目标程序体积较大。
注意:
1、频繁使用的函数
2、函数代码语句很少(<= 5局)
3、函数代码语句简单,没有控制语句(循环语句,switch)
1.6.6、内联函数语法
inline存储类型数据类型函数名(参数列表)
1.6.7、内联函数的限制
使用内联函数可以节省运行时间,但却增加了目标程序的长度。
1.6.8、函数重载
即对一个函数名重新赋予它新的含义,使一个函数名可以多用。
1.6.9、函数模板
c++中的函数重载,函数名相同,但参数列表必须不同(1)、参数个数不同
(2)、参数个数相同、类型不同
使用的时候看实参来调用,函数的返回值不可以作为函数重载的条件
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。
1.6.10、函数默认参数
即在调用该函数时,可以不写某些参数的值,编译器会自动把默认值传递给调用语句中。
默认值只能在声明中设置。
1.7、再谈结构体
C++ 语法中相对C语语法增加了访问杈限的概念,
有三种: public、prⅳate及protected,默认是 public。
public:公共成员,表示可以通过结构体变量对象直接访问到成员
private:私有成员,表示仅结构体成员函数可以使用的成员
protected:保护成员,表示被继承的派生对象可以访闷使用的成员
二、内存模型及名字空间
2.1、作用域
2.1.1、作用域
作用域( scope)描述了一个名字在文件(编译单元)的多大范围内可见。
2.1.2、C++支持三种形式的域
(1)、局部域 (local scope)
局部域是包含在函数定义或者函数块中的程序文本部分。
(2)、名字空间域 (namespace scope)
局部域是包含在函数定义或者函数块中的程序文本部分。
(3)、类域 (class scope)
局部域是包含在函数定义或者函数块中的程序文本部分。
2.1.2、变量的作用域
局部变量的作用域为局部,仅能在代码块内可见。
2.1.3、函数的作用域
可以是整个类,也可以是整个名字空间,但不能是局部的。
2.2、链接性及存储性
2.2.1、链接性 (linkage)描述了名称如何在各个单元中的共享。
2.2.2、外部链接:是指名称可以在文件间共享。
2.2.3、内部链接:名称仅仅能在一个文件中的函数共享。
2.2.4、变量链接及存储性
2.2.5、变量的说明限定符:auto register static extern
2.2.6、函数的链接性及存储性:函数的存储持续性为静态的。
存储方式:4种: auto register static extern
2.2.7、语言链接性:
对于函数或变量而言,只要有链接性,则每种编程语言,都有自己的规则来处理它们的名字。这个就是语言的链接性
2.3、动态内存
2.4、声明区与作用域
C++标准的名字空间属性
声明区:可以进行声明的区域
潜在的作用域:变量潜在的作用域,从声明点,到声明区结尾
2.5、名字空间
三、输入输出流
3.1、概述
C++的输出和输入是用“流”(stream)的方式实现的。
在定义流对象时,系统会在内存中开辟一段缓冲区,用来暂存输入输出流的数据。
在执行输入输出语句时,先把数据顺序存放在输出缓冲区中,直到输出缓冲区满或遇到输入输出语句中的endl(或′\n′,ends,flush)为止,此时将缓冲区中已有的数据一起输出,并清空缓冲区。
3.2、C++输出
输出流即从程序中输出一系列的字符串,使用cout对象
3.3、C++输入
输入流即向程序输入一系列的字符串,使用cin对象
3.4、格式控制符
四、类和对象
4.1、OPP思想
4.1.1、抽象
抽象就是声明定义一个类,抽象概括一类对象的公共性质。
数据抽象:设置成员变量
代码抽象:设置成员函数
抽象的实现:通过类的定义
4.1.2、封装
封装将数据成员和成员函数结合在一起,形成一个整体,就是类体部分。
4.1.3、分类
层层分类,使概念逐渐细化、具体化。
4.1.4、继承
子类继承父类所有的属性、方法,并可定义自己的特有属性与方法。
4.1.5、多态
多态性是面向对象程序设计的一个重要特征,能增加程序的灵活性。
4.2、类的声明定义
C++中对象的类型称为类(class),类代表了某一批对象的共性和特征,类是对象的抽象,而对象是类的具体实例 (instance)。
(1)、编写一个的类基本语法形式:
class 类名{
private:
私有的数据和成员函数;
public:
公有的数据和成员函数;
protected:
保护的数据和成员函数;
};
#include
using namespace std;
class Demo{
public:
int a;
int setx(int val);
#if 0
int setx(int val)
{
x = val;
}
#endif
int getx()
{
return x;
}
protected:
int b;
private:
int c;
int x;
};
int Demo::setx(int val)
{
x = val;
return 0;
}
int main(int argc, char *argv[])
{
Demo obj;
obj.setx(123);
cout << obj.getx() << endl;
return 0;
}
//cout:123
4.3、构造和析构
4.3.1、构造函数
构造函数是一种特殊的成员函数,与其他成员函数不同。
#include
using namespace std;
class Demo{
public: //公有
Demo(int val) //构造函数:无返回值,函数名与类名相同,可以有形参,定义对象的时候自动调用
{
myval = val;
cout << "func: " << __func__ << "line: " << __LINE__ << endl;
}
int getval()
{
return myval;
}
private: //私有
int myval;
};
int main(int argc, char *argv[])
{
Demo obj(123); //构造对象并且传递参数
cout << obj.getval() << endl;
return 0;
}
4.3.2、析构
#include
using namespace std;
class Demo{
public: //公有
Demo(int val) //构造函数:无返回值,函数名与类名相同,可以有形参,定义对象的时候自动调用
{
myval = val;
cout << "func: " << __func__ << "line: " << __LINE__ << endl;
}
~Demo() //析构函数:无参数,无返回值
{
cout << "func: " << __func__ << "line: " << __LINE__ << endl;
}
int getval()
{
return myval;
}
private: //私有
int myval;
};
int main(int argc, char *argv[])
{
Demo obj(123); //构造对象并且传递参数
cout << obj.getval() << endl;
return 0;
}
4.3.3、拷贝构造
1、定义对象的一般格式
2、动态创建对象
cp2
3、函数的传值调用
cp3
4.3.4、浅拷贝和深拷贝
(1)、浅拷贝:同类型的两个对象的成员指针,指向同一个堆区空间,两成员指针相互干扰
(2)、深拷贝:同类型的两个对象的成员指针,指向两个不同的堆区,互不干扰
4.4、this指针
this指针是一个特殊的指针,指向类对象自身的首地址。
4.5、static成员
4.4、const成员
4.4.1、const成员函数
在类内声明语法形式
<数据类型> <函数名> (<参数列表>) const;
在类外定义语法形式
<数据类型> <类名> :: <函数名> (<参数列表>) const
{
……
}
4.4.2、const对象
#include
#include
using namespace std;
class Demo{
public:
Demo(int a=0, int b= 0):x(a), y(b)
{
cout << "line:" << __LINE__ <
4.4、友元
类的朋友
4.4.1、友元函数
指:某个函数是类的友元;
一个类的函数是多个类的函数
4.4.2、友元类
#include
#include
using namespace std;
class A{
public:
A(int val=0):myval(val)
{
cout<<__LINE__<myval; //访问A类的私有成员
}
int getval()
{
return myval;
}
private:
int myval;
};
int main(int argc, char *argv[])
{
A obj1(123);
B obj2(321);
obj2.setval(obj1); //通过obj2修改obj1的成员1变量值
cout<
4.4.3、友元成员函数
B类的成员函数可以是类A的友员
#include
using namespace std;
class A; //前向 声明
class B{
public:
B(int val=0) :myval(val)
{
cout<<__LINE__<myval; //
}
int main()
{
A obj(123);
B obj2(321);
obj2.setval(obj);
cout<
五、运算符重载
5.1、为什么需要重载运算符
函数可以重载,运算符也可以重载。
运算符重载:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
5.2、运算符重载简介
5.2.1、语法形式
运算符重载的一般语法如下:
<返回类型说明符> operator <运算符符号>(<参数表>)
{
<函数体>
}
5.2.2、可以被重载的运算符
算术运算符:+、-、*、/、%、++、--
位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]
5.2.3、不被重载的运算符
==不能被重载的运算符,其余的都可以被重载==
逗号“.”、?号表达式“? :”、sizeof、作用域“::”、指针运算符“*”
#include
#include
using namespace std;
int main()
{
string a = "hello";
string b = "world";
cout << a+b << endl;
return 0;
}
5.3、友元运算符重载
5.3.1、含义
指:运算符重载作为类的友元
5.3.2、语法形式
友元运算符重载的语法形式:
class 类名
{
friend 返回类型 operator 运算符(形参表);
}
类外定义格式:
返回类型 operator 运算符(参数表)
{
函数体;
}
5.3.3、四则运算符运算
/*四则运算符运算*/
#include
using namespace std;
class Demo{
public:
Demo(int val):myval(val)
{
cout<<__LINE__<myval;
}
friend Demo operator + (Demo &obj1, Demo &obj2);
friend Demo operator - (Demo &obj1, Demo &obj2);
friend Demo operator * (Demo &obj1, Demo &obj2);
friend Demo operator / (Demo &obj1, Demo &obj2);
private:
int myval;
};
//重载运算符 '+'
//'+' 是双目运算符,需要两个参数
Demo operator + (Demo &obj1, Demo &obj2)
{
Demo obj3(obj1.myval + obj2.myval); //运算obj1和obj2的和然后再通过构造函数
return obj3;
}
Demo operator - (Demo &obj1, Demo &obj2)
{
Demo obj3(obj1.myval - obj2.myval); //运算obj1和obj2的和然后再通过构造函数
return obj3;
}
Demo operator * (Demo &obj1, Demo &obj2)
{
Demo obj3(obj1.myval * obj2.myval); //运算obj1和obj2的和然后再通过构造函数
return obj3;
}
Demo operator / (Demo &obj1, Demo &obj2)
{
Demo obj3(obj1.myval / obj2.myval); //运算obj1和obj2的和然后再通过构造函数
return obj3;
}
int main(int argc, char *argv[])
{
Demo a(8);
Demo b(2);
Demo c = operator + (a, b); //Demo c = a+b;
Demo d = operator - (a, b); //Demo d = a-b;
Demo e = operator * (a, b); //Demo d = a*b;
Demo f = operator / (a, b); //Demo d = a/b;
cout << a.getval() << endl;
cout << b.getval() << endl;
cout << "a + b: " << c.getval() << endl;
cout << "a - b: " << d.getval() << endl;
cout << "a * b: " << e.getval() << endl;
cout << "a / b: " << f.getval() << endl;
return 0;
}
/*
cout:
15
15
15
15
15
15
8
2
a + b: 10
a - b: 6
a * b: 16
a / b: 4
*/
5.3.4、”++”运算符
/*前++与后++的实现*/
#include
using namespace std;
class Demo{
public:
Demo (int x) :myval(x){ cout << __LINE__<myval;
}
friend Demo operator ++( Demo &obj, int ); //后 ++ int 作为 占位符
friend Demo operator ++( Demo &obj );
private:
int myval;
};
//重载运算符 "++"
// ++ 单目运算符 需要指定一个参数
#if 1
Demo operator ++( Demo &obj, int val ) //后++
{
Demo obj2(obj.myval++);
return obj2;
}
#endif
#if 1
Demo operator ++(Demo &obj ) //前 ++
{
Demo obj2(++obj.myval);
return obj2;
}
#endif
int main()
{
Demo a(222);
Demo b(444);
/*
a++; //等价 operator ++ (a);
Demo c = a++;
*/
Demo c = ++a;
cout << a.getval()<
5.3.5、”!”运算符
/*!运算*/
#include
using namespace std;
class Demo{
public:
Demo (bool flag = true){
this->flag = flag;
cout << __LINE__<
5.4、成员函数运算符重载
第一个参数类型和对象类型匹配,可以使用this 指针,那么形参个数可以 减 1。
5.4.1、语法形式
成员函数运算符重载的语法形式:
class 类名
{
返回类型 operator 运算符(形参表);
}
类外定义格式:
返回类型:: operator 运算符(形参表)
{
函数体;
}
5.4.2、”+=”运算符
/*+=运算*/
#include
using namespace std;
class Demo{
public:
Demo(int x):myval(x)
{
cout <<__LINE__<myval;
}
//重载运算符 +=
//+=:双目运算符,由于该重载函数是类的成员,则之需要传一个参数即可,
//另一个参数可以用this直接访问
//a+=b; 返回a
Demo & Demo::operator +=(Demo &obj) //内部成员函数实现重载 有this 指针, 可以用this ,传参是可以少一个
{
this->myval += obj.myval;
return (*this); //返回this 指针指向的对象
}
int main()
{
Demo a(222);
Demo b(444);
a += b; //等价 a.operator +=(b); //a = a + b
cout <
5.4.2、友元成员函数
(1)、用友元重载+实现对象与整型数相加 Demo b(5):
b+3;
(2)、成员函数重载实现b+3。
#include
using namespace std;
class Demo{
public:
Demo(int x):myval(x)
{
cout <<__LINE__<myval;
}
#if 1
/*内部成员函数实现运算符重载 有this 指针 ,传参是可以少一个
*
*行参个数 = 运算符操作个数 - 1
*/
int Demo::operator + (int val)
{
return this->myval + val; //返回this 指针指向的对象
}
#endif
/*友元运算符重载:因为友元函数没有this 指针, 所以对应的重载运算符 需要多少个操作数 那则传递多少个 参数
*
*
*/
#if 0
int operator + (Demo &obj, int val) // 参是可以少一个
{
return obj.myval + val; //返回this 指针指向的对象
}
#endif
int main()
{
Demo a(222);
Demo b(444);
// cout << 3 + b << endl; //(b, 3);
cout << b + 3 << endl; //(b, 3);
return 0;
}
/*友元运算符重载和成员运算符重载 函数选择:
* 1、如果成员函数的this 指针指向运算符的第一操作数的类型, 与当前对象类型匹配才可以用 this 指针
* 2、否则就使用友元运算符重载
*
*/
/*
cout:
8
8
447
*/
5.4.2、左移、输出
#include
using namespace std;
class Demo{
public:
Demo (int x):myval(x) { cout << __LINE__<myval;
}
Demo Demo::operator << (int bits) //运算符重载 << :左移 运算
{
#if 0
Demo tmp(this->myval << bits);
return tmp;
#endif
return Demo (this->myval << bits);
}
ostream & operator << (ostream & out, Demo &obj )
{
out <<" ______________" <
5.4.2、”()”括号””运算符;成员
==特殊:”()”不能是友元,必须是成员==
#include
using namespace std;
class Demo{
public:
Demo (int x) :myval(x)
{
//cout << __LINE__<myval + a;
}
#if 1
#if 1
int operator () (int a)
{
return myval + a;
}
#else
int operator () (int a, int b)
{
return a + b;
}
#endif
#endif
// friend int operator ()(Demo &obj, int a); // error : ()只能是成员, 不能是友元
private:
int myval;
};
/*
int operator ()(Demo &obj, int a)
{
return obj.myval + a;
}
*/
int main()
{
Demo a(5); //初始化
/*
cout << a + 3 << endl; //cout <
5.5、运算符重载注意事项
A、除关系运算符"."、成员指针运算符".*"、作用域运算符"::"、sizeof运算符和三目运算符"?:"以外,C++中的所有运算符都可以重载(其中“=”和“&”不必用户重载)。
B、重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
C、运算符重载的实质是函数重载,遵循函数重载的选择原则。
D、重载之后的运算符不能改变运算符的优先级和结合性,也不能改变运算符操作数的个数及语法结构。
E、运算符重载不能改变该运算符用于内部类型对象的含义。
F、运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
G、重载运算符的函数不能有默认的参数,否则就改变了运算符的参数个数。
H、重载的运算符只能是用户自定义类型,否则就不是重载而是改变了现有的C++标准数据类型的运算符的规则。
I、运算符重载可以通过成员函数的形式,也可是通过友元函数,非成员非友元的普通函数。
六、模板
6.1、什么是模板
6.2.1、模板简介
C++中模板是支持参数化多态的工具,就是让类或者函数声明为一种通用类型,使得类中的某些数据成员或者成员函数的参数、返回值在实际使用时可以是任意类型。
使用模板的目的就是能够让程序员编写与类型无关的代码,模板也是泛型编程的基。
6.2.2、模板通常两种形式
模板是一种对类型进行参数化的工具,通常有两种形式:
函数模板:
函数模板针对仅参数类型不同的函数
类模板:
类模板针对仅数据成员和成员函数类型不同的类
6.1.3、函数模板
仅针对函数参数类型,返回值不同,功能相同的函数
template < class形参名, class形参名> 返回类型 函数名(参数列表)
{
函数体
}
#include
using namespace std;
//声明及定义 类型模板函数
template //typename 可以class 代替
T add(T a, T b)
{
return (a + b);
}
int main(int argc, char *argv[])
{
double e = add(1.2, 3.4); //根据返回值 类型自动匹配 T 为 double
cout << "e: " << e << endl;
int sum = add(5, 6); //根据返回值 类型自动匹配 T 为 int
cout << "sum: " << sum << endl;
//cout << add(10, 1.1) << endl; //error:自动匹配的时候 没有匹配到 10的int和1.1的double
//因为没有定义两个类型的函数模板
cout << "add: " << add(10, 1.1) << endl; //添加 add 即可解决
return 0;
}
/*
cout:
e: 4.6
sum: 11
add: 11.1
*/
#include
using namespace std;
class Demo{
public:
Demo (int x):myval(x)
{
cout << __LINE__ << endl;
}
~Demo()
{
cout << __LINE__ << endl;
}
public:
int getval()const
{
return this->myval;
}
Demo operator + (Demo &obj) //成员运算符重载
{
cout << "++++" << endl;
return Demo(this->myval + obj.myval);
}
private:
int myval;
};
/*模板说明:
*template
* template 模板说明关键字
* class 和 typename 一样 类型说明关键字
* T : 模板行参 用来替代 数据类型
*/
//声明及定义 类型模板函数 必须使用 template 说明
template //typename 可以用class代替
T add(T &a, T &b)
{
return (a + b);
}
int main(int argc, char *argv[])
{
Demo a(222);
Demo b(444);
Demo c = add(a, b);
cout << c.getval() << endl;
return 0;
}
/*
cout:
15
15
++++
15
666
19
19
19
*/
#include
using namespace std;
//template T add(T , T); //函数模版声明
#if 0
template //函数模版的定义
B add( B x, B y )
{
return x + y;
}
#else
template
T1 add( T2 x, T3 y )
{
return x + y;
}
#endif
int main()
{
#if 0
cout << add(1, 3) <(2,3) << endl; //函数模版调用,自动匹配数据类型
cout << add< >(2.1,3.1) << endl; //函数模版调用,自动匹配数据类型
cout << add< double >(2,3) << endl; //函数模版调用显式转换为double
cout << add< double >(4, 3.1) << endl; //函数模版调用, 显示指定返回值类型为 double
cout << add< >(2,3) << endl; //函数模版调用,自动匹配数据类型
#endif
cout << add< int>(2,3) << endl; //函数模版调用,显示指定返回值类型为 int
cout << add< double>(2,3) << endl; //函数模版调用,需要显示指定 返回值类型 double
cout << add< double >(1, 3.1) <(2.7 , 3.3) << endl; //函数模版调用, 第一个类型匹配万为 返回值类型,后两个为参数类型, 如果参数类型不匹配,会发生强转后再计算 然后返回指定的 返回值类型
return 0;
}
/*
cout:
5
5
4.1
5.7
*/
6.1.4、类模板
类模板针对仅数据成员和成员函数类型不同的
template < class形参名, class形参名,…> class 类名
{
…
}
template
template: 关键字,说明是一个模板
class T : 定义类型模板形参,class 等价于 typename
T : 类型模板形参,用来替换数据类型
注意:
模板声明和全局只能在全局。
6.2、类型模板参数
指类中成员方法返回值类型 参数类型 及 成员变量类型为通用类型
语法形式:
template
class 类名
{
public:
T 方法名(T 参数名)
{
语句块;
}
private:
T 成员变量名;
};
#include
using namespace std;
//定义 类模版
template //声明类型模版 T
class Demo{
public:
Demo( T val){
myval = val;
} //由于 T 是通用 类型,所以 构造函数不能指定默认参数值
~Demo(){};
public:
void setval (T val);
T getval() const;
private:
T myval;
};
//类外部成员方法实现
template //声明类型模版 T
void Demo ::setval( T val )
{
myval = val;
}
template //声明类型模版 T
T Demo::getval()const
{
return myval;
}
int main()
{
Demo obj(123);
// Demo <> obj(222);
obj.setval(666);
cout << obj.getval() <
注意:
1> 模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。
2> 类型形参仅由关键字class或typename后接说明符构成
3> 譬如函数模板
template T func(T var)
{
…
}
template< typename T> T func(T var)
{
…
}
其中T就是一个类型形参
形参var及返回值类型为T,实际类型在实例化时确定。
6.3、非类型模板参数
模板的非类型形参就是内置类型形参
譬如:
template class Demo
{
…
};
其中int var就是非类型的模板形参。
非类型形参在模板定义的内部是常量值。
非类型模板的形参只能是整型、指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。非类型模板形参的实参如果是表达式,则必须是一个常量表达式,在编译时计算出结果。
非类型模板形参和实参间允许类型转换。
#include
#include
using namespace std;
//非类型模版参数
template //int len 是非类型行参,指定数组元素个数
class Array{
public:
Array(T val);
~Array();
public:
void setval(const int pos, const T val); //设置对应下标的元素
T getval( const int pos ) const; //获取对应下标的元素值
private:
T *buffer; //保存数组首地址
};
template
Array::Array(T val) //构造函数实现
{
cout << __func__<< "line:"<<__LINE__<
Array::~Array() //析构函数的实现
{
cout << __func__<< "line:"<<__LINE__<
void Array::setval(const int pos, const T val) //设置对应下标的元素值
{
buffer[pos] = val;
}
template
T Array ::getval(const int pos) const //获取对应下标的元素值
{
return buffer[pos];
}
int main()
{
Array obj(0);
for(int i=0; i<10 ;i++)
{
obj.setval(i, i+1);
}
for(int i=0; i<10 ; i++)
{
cout<
6.4、默认模板参数
类模板的类型形参可以有默认值,函数模板的类型形参则不能。
函数模板和类模板都可以为模板的非类型形参提供默认值。
类模板的类型形参默认值形式为:
template class Demo
{
…
};
类型形参T1、T2,其中T2的默认类型为int。
类模板类型形参默认值和函数的默认参数一样,如果有多个类型形参则从左起的莫个形参设定了默认值之后,右边所有模板形参都要设定默认值
和函数默认参数一样,如果在类模板的外部定义类中的成员时,模板形参表应省略默认的形参类型。
#include
#include
using namespace std;
//默认类型参数模版
//template //int len 是非类型行参,指定数组元素个数
template //指定默认参数
class Array{
public:
Array(T val);
~Array();
public:
void setval(const int pos, const T val); //设置对应下标的元素
T getval( const int pos ) const; //获取对应下标的元素值
private:
T *buffer; //保存数组首地址
};
template
Array::Array(T val) //构造函数实现
{
cout << __func__<< "line:"<<__LINE__<
Array::~Array() //析构函数的实现
{
cout << __func__<< "line:"<<__LINE__<
void Array::setval(const int pos, const T val) //设置对应下标的元素值
{
buffer[pos] = val;
}
template
T Array ::getval(const int pos) const //获取对应下标的元素值
{
return buffer[pos];
}
int main()
{
//Array obj(0);
Array< > obj(0); // 指定默认参数,可以具体参数
for(int i=0; i<10 ;i++)
{
obj.setval(i, i+1);
}
for(int i=0; i<10 ; i++)
{
cout<
6.5、友元函数模板
如果一个类是模板类,又要实现运算符重载,一般的,运算符重载是友员函数,那么显然会涉及到一个问题,一个友员如何操作模板类。
其实质就是类模板和函数模板的综合应用。
#include <iostream>
#include <assert.h>
using namespace std;
/*友元模板*/
template <class T> //类 模板
class Demo{
public:
Demo(T x);
#if 0
friend T operator + (Demo <T> &obj1, Demo <T>&obj2)
{
return obj1.x + obj2.x;
}
#else
template <class B>
friend B operator + (Demo <B> &, Demo <B> &);
#endif
private:
T x;
};
template <class B>
Demo <B>::Demo(B x):x(x){}
template <class B> //函数模板 和 类模板结合成为函数模板
B operator + (Demo <B> &a, Demo <B> &b)
{
return a.x + b.x;
}
/*
*友元函数模板
* 类模板中的友元运算符重载函数,在类外部定义时,必须单独说明该函数为一个函数模板
* 由于友元函数不是类的成员函数,相当于定义了一个普通的函数模板
* 因此,在类中声明时,也必须使用template关键字声明
*
* 友元函数模板 实质是类模板的结合
*
*
*/
int main()
{
Demo <int> a(1), b(2);
cout << a + b << endl;
return 0;
}
/*
cout:
3
*/
七、类继承
7.1、概念
在进行C编程的时候,代码重用的目的就是提高开发效率、减少错误
、让大规模代码开发的关注点转到软件结构上。
C++(OOP)的代码重用除了简单层次的提供类库,还提出了更高层次:
类继承( inheritance)、多态( Polymorphism)、泛型编程( Generic Programming),等等。
在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。
学生称为基类或父类
新建如:小学生称为派生类或子类
7.2、派生一个类
7.2.1、语法形式
class子类名:权限访问限定符 基类名1,权限访问限定符 基类名2,...
{
//class body
};
7.2.2、权限访问符
c:表示基类的 public/ protected成员可以被子类访问,通过子类对象可访问基类的 oublic成员
tected/private:表示基类的 public/ protected.减员可以被子类访问,通过子类对象不能访问基类
#include
using namespace std;
#define pri() cout <<__func__<<"line:"<<__LINE__<
/*子类父类重名*/
#include
using namespace std;
class Father{
public:
double money;
void show(){
cout << __func__<<__LINE__<
/*保护继承*/
#include
using namespace std;
class Father{
public:
double money;
void show(){ ; }
protected:
void setMoney(double m)
{
money = m;
}
private:
int age;
};
class Son: protected Father{ //保护继承 和私有继承 都不能通过子类外部 对象访问,但是可以通过类内部访问,
//class Son: private Father{ //如果变为 私有继承,那么 该 Son 被再次继承时,不能访问 任何Father 成员成员
public:
int showMoney()
{
money = 20; //公有成员 可以通过类内部访问
setMoney(10000); //保护 可以通过类内部访问
//age =18; // 私有 不能访问
}
protected:
int x;
private:
char idCard[18];
};
class Son2 :public Son{
public:
int showMoney()
{
money = 20; //公有成员 可以通过类内部访问
setMoney(10000); //保护 可以通过类内部访问
//age =18; // 私有 不能访问
}
};
int main()
{
Son obj;
#if 0
obj.money = 16000; //通过类的外部对象都不能访问
obj.show();
obj.setMoney(20000);
obj.age = 18;
#endif
return 0;
}
/*
cout:
*/
有三种继承方式:公有继承、保护继承、私有继承。
派生类访问基类的成员的权限如下表所示:
7.3、派生类的构造及析构
派生类不能继承基类的构造jc、析构函数。
派生类有自己的构造、析构函数。
如果基类构造函数有参数,在从派生类的构造函数把参数传递给基类的构造函数。.
派生类名∷构造函数名(参数列表):基类名(参数列表) 。
- 继承时,构造顺序:先基类构造,再派生构造
析构顺序:先派生类析构,再基类构造 - 包含对象成员的派生类和基类构造顺序:
先 基类构造,再对象成员构造,最后派生类构造 - 析构顺序与构造顺序相反
#include
using namespace std;
#define pri() cout << __func__ << "line:"<<__LINE__<< endl;
class Base{
public:
Base(){pri(); };
Base(int x):x(x) {pri();}
~Base(){pri(); }
int x;
};
class Subclass: public Base{
public:
//Subclass (int x) :Base(x), a(x) // 显示调用
Subclass (int x) : a(x) //隐式调用默认构造
{
pri();
}
~Subclass(){pri(); }
private:
int a;
};
int main()
{
Base b(3);
Subclass s(5);
cout << s.x <
注意:类体中显示说明,带参数的构造函数,那么无参数的默认构造 不会自动生成
- 派生类构造函数参数初始化列表中,隐式调用基类 默认构造(无参数的构造函数)
- 如果基类出现带参数构造函数,必须在派生类的构造函数参数初始化列表中,显示调用 基类带参构造函数
- 如果派生对象成员的构造函数带参数,必须在派生类的构造函数参数初始化列表中,为对象成员这个变量赋初值。
#include
using namespace std;
#define pri() cout << __func__<<"line:"<<__LINE__<
7.4、is-a关系
is-a关系
is-a 一般是继承关系,has-a一般是组合。
- is-a 派生类对象也是一个基类对象,基类对象能够做的操作,派生类也可以
- has-a 是一种聚合,表示一个类中有另一个类的对象。
7.5、多重继承
多重继承的路径二义性:
解决方式,作用域访问符 和 设置虚基类(虚继承(继承时有 virtual)共同的基类叫虚基类
/*向上向下隐式转换*/
#include
using namespace std;
class Base{
public:
Base (int x ):x(x) //这里的 第一次 x = 10
{
cout<< "base" <<__LINE__<x << endl;
//delete q;
//q = NULL;
// delete p;
// p = NULL;
//
Base *m = new Subclass(11, 22);
Base &a = s;
cout << m->x <
/*狼人*/
#include
using namespace std;
#define pri() cout << __func__<<__LINE__<
虚基类
#include
using namespace std;
#define pri() cout << __func__<<__LINE__<
八、多态
8.1、什么是多态
即多态为: 一种方法,多种实现
多态(Polymorphism)按字面的意思就是“多种状态”,简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,是面向对象编程领域的核心概念。
即多态为:一种方法,多种实现
;
多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。
在面向对象编程(OOP)的主要特征
封装wrap:实现细节隐藏,使得代码模块化。把成员数据和成员函数封装起来,通过公共的成员接口进行成员数据的操作。
继承inheritance:扩展已存在的代码,目的是为了代码重用
多态polymorphism:目的是为了接口重用。也就是说,不论传递过来的究竟是哪个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。
#include
using namespace std;
#define pri() cout << __LINE__<prnmsg();
}
int main()
{
Base b; // 基类对象
Subclass s;// 派生类对象
test(&b); // 调基类 Base obj = b
test(&s); //调派生类 Base obj = s //向上隐式转换 还是 Base
/*会不会实现传什么调什么
*在这里 如果不用多态 不能实现传什么调什么 始终调基类
*/
// s.prnmsg(); //派生类调用
//s.Base::prnmsg();
return 0;
}
/*
cout:
7
12
*/
8.2、虚函数
有时候,希望子类和基类有相同的方法,但是行为却有所不同,这就是多
文里就引入了虚函数的概念。
有时候,希望子类和基类有相同的方法,但是行为却有所不同,这就是多态。这里就引入了虚函数的概念。
注意:简单继承,is-a的关系不是多态;另外函数重载,一般应该是行为类似,使用方法不同,也不是多态。
简单地说,用virtual修饰的成员函数,就是虚函数。虚函数的作用就是实现多态性(Polymorphism)。多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。
虚函数的限制如下:
A、非类的成员函数不能定义为虚函数
B、类的静态成员函数不能定义为虚函数
C、构造函数不能定义为虚函数,但可以将析构函数定义为虚函数
D、只需要在声明函数的类体中使用关键字“virtual”将函数声明为虚函数,而定义函数时不需要使用关键字“virtual”。
E、当将基类中的某一成员函数声明为虚函数后,派生类中的同名函数(函数名相同、参数列表完全一致、返回值类型相关)自动成为虚函数。
8.2.1、虚函数语法
class 类名{
public:
virtual 返回值类型 函数名1(参数列表)
{ }
…
virtual 返回值类型 函数名n(参数列表)
{ }
};
注意:(重点)
虚函数是在运行时根据对象决定调用的函数;
8.3、覆盖、重载、隐藏
8.4、动态联编(动态链接)
8.4.1、联编(链接)
就是将模块或者函数合并在一起生成可执行代码的处理过程。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。
静态联编(静态链接)
是指在编译阶段就将函数实现和函数调用关联起来,因此静态联编也叫早绑定。
动态联编(动态链接)
是指在程序执行的时候才将函数实现和函数调用关联,因此也叫运行时绑定或者晚绑定。
C++中一般情况下联编也是静态联编,但是一旦涉及到多态和虚拟函数就必须要使用动态联编了。
重载只是一种语言特性,编译器根据函数不同的参数表,把同名函数区分开来,属于静态联编,与多态无关。引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
8.4.2、静态联编(静态链接)
8.4.3、动态联编(动态链接)
#include
using namespace std;
class Base{
public:
Base(int x, int b):x(x), b(b){} //构造函数 参数初始化列表 给成员变量 付初始值
virtual void func1(){
cout << "line: " << __LINE__< 0x1234
p(); //得到 调用虚函数 func 1说明 可以通过虚函数表首地址 第一个成员就算func1的入口地址
p = (FUNC) *((int *)vtbl+1); //下一个能的得到 func2的调用 更加确定我结论的正确性
p();
cout<< sizeof(obj)<
8.5、抽象类
含有纯虚函数的类就是抽象类。
抽象类没有完整的信息,只能是派生类的基类
抽象类不能有实例,不能有静态成员
派生类应该实现抽象类的所有方法
/*纯虚函数*/
#include
using namespace std;
class Base{
public:
//virtual void prnmsg() ; //虚函数
virtual void prnmsg() = 0; //纯虚函数(包含纯虚函数的累就是 抽象类)
protected:
int y;
private:
int x;
};
class Subclass:public Base{
public:
void prnmsg(){
cout <<__LINE__<
8.6、虚继承
虚继承解决多重继承产生的二义性;
8.7、虚析构函数
一般将析构函数指定为虚的,主要是避免空间回收不完整;
#include
using namespace std;
#define pri() cout<<__func__<<__LINE__<
8.8、限制构造函数
1> 含义
指 构造函数的权限不是publlic,而是protected/private
2>友元函数解决限制构造函数不能构造对象的问题
#include
using namespace std;
#define pri() cout<<__func__<<__LINE__<prnmsg();
deleteobj(p);
return 0;
}
/*
cout:
Base8
prnmsg13
~Base9
*/
8.9、类的大小
/*求类的大小*/
#include
using namespace std;
class Base{
};
class Demo{
char buf[3]; // 4 4
short a; // 4 2
};
class Demo1{
double buf[3]; // 24
char a; //
char b;
//char c;
int f;
short d;
};
class Demo2{
char a;
double s;
int c;
short b;
};
class Demo3{
long buf[3]; // 12
char str[9]; // 12
short bu[5];
double a; //8
};
class Demo4{
long buf[3]; // 12
char str[9]; // 12
double a; //8
void setval();
};
class Demo5{
virtual void setval(int x){} // 4 4
virtual void setval2(int x){} // 0 4
void setval3(int x){} // 0 0
int x;
};
class Demo6{
int b; // 8 12
static int a;
friend void get(){}
virtual void set(){}
int x;
};
int Demo6 ::a=13;
class Demo7:public Demo6{
};
class Demo8:public Demo6, public Demo5{ //12 8
int x;
virtual void fun(){}
};
class Demo9:public virtual Demo6, public virtual Demo5{ //12 8
int x;
int b;
// virtual void fun(){}
};
int main()
{
cout << "sizeof(Base)=" << sizeof(Base) << endl;
cout << "sizeof(Demo)=" << sizeof(Demo) << endl;
cout << "sizeof(Demo1)=" << sizeof(Demo1) << endl;
cout << "sizeof(Demo2)=" << sizeof(Demo2) << endl;
cout << "sizeof(Demo3)=" << sizeof(Demo3) << endl;
cout << "sizeof(Demo4)=" << sizeof(Demo4) << endl;
cout << "sizeof(Demo5)=" << sizeof(Demo5) << endl;
cout << "sizeof(Demo6)=" << sizeof(Demo6) << endl;
cout << "sizeof(Demo7)=" << sizeof(Demo7) << endl;
cout << "sizeof(Demo8)=" << sizeof(Demo8) << endl;
cout << "sizeof(Demo9)=" << sizeof(Demo9) << endl;
return 0;
}
/*计算类的空间大小 :类似结构体按字节对齐
* 1、空类 占 1 个字节 (站位符)
* 2、成员函数 、静态成员、 友元函数 占 0字节
* 3、虚函数 占 4字节 (仅虚函数表占4字节)
* 4、非静态成员 满足字节序对齐
*/
/*
sizeof(Base)=1
sizeof(Demo)=6
sizeof(Demo1)=36
sizeof(Demo2)=20
sizeof(Demo3)=40
sizeof(Demo4)=32
sizeof(Demo5)=8
sizeof(Demo6)=12
sizeof(Demo7)=12
sizeof(Demo8)=24
sizeof(Demo9)=32
*/
九、异常
9.1、什么是异常
- 什么是异常:异常在一种容错机制,是一种错误处理系统。
- 为什么要有异常:保证软件系统运行的稳定性与健状性
- C++的异常处理机制有3部分组成try(检查)→ throw(抛出)> catch(捕获)
try{
//检查语句
if(错误){
throw异常
}
} catch(异常类型1){
进行异常处理的语句1
}
…
#include
#include //标准异常的头文件
using namespace std;
/*c++ 中默认每一个函数都会抛出异常 除非特殊请说明
* 如果要提示抛出什么类型的异常,那么就必须在函数的声明处 用 throw (。。)说明
*
*如果 需要说明该函数没有异常,是安全的函数,则声明定义时,就可以用throw() 或者
noexecpt来说明
*
*/
int fun(int ,int) throw(invalid_argument); //告知 编译器或用户 该函数会抛出什么类型的异常
int fun(int x ,int y) throw(invalid_argument)
{
if(!y) //检查错误
throw invalid_argument("参数有误:y==0"); //抛出异常并携带错误信息(实质是抛出一个类对象)
else
return x/y;
cout << "------------";
}
int main()
{
int a, b;
cin >> a >> b;
try{ //检查错误
cout<< fun(a,b) << endl;
// }catch ( const invalid_argument &err ){ //捕获异常、定义一个异常类型的对象来接收
//通过const 为常量取别名 为 &err
//异常处理(这里是没有自定义处理方式,默认交由上级处理)
}catch ( invalid_argument err ){ //捕获异常、定义一个异常类型的对象来接收 不引用 定义同类类型接收
cout << err.what() <
9.2、使用标准异常
C++ 中默认每一个函数都会抛出异常,如果要说明该函数不会抛出异常,那么声明和定义时,都用 throw()
如果函数说明为 不会抛出异常,那么内部强行抛出异常,会出现终止程序运行
throw() :是C++ 旧标准的
noexcept:是C++ 11 标准中的运算符,32位机中编译时,必须使用 -std=c++0x
int func(int, int)throw();
9.3、自定义异常
在设计一个大系统的时候,往往设计者会自定义很多错误,这些错误在标准错 误里面是没有的,那么就需要我们来设计一些异常类
#include
using namespace std;
#include
#include
int fun(int , int);
int fun(int x, int y)
{
if(!y)
throw invalid_argument("y==0"); //抛出匿名对象 (没有对象名数据)
else if(y == 1)
{
invalid_argument temp("y==1");
throw temp;
}
else
return x/y;
cout << "------"<> a >> b;
try{
cout << fun(a, b) <
9.4、异常规范
十、转换函数
10.1、什么是转换函数
#include
using namespace std;
class Demo{
public:
Demo(int x):x(x){}
friend ostream & operator << (ostream & out, Demo &obj) //运算符重载
{
out << obj.x;
return out;
}
private:
int x;
};
class Base{
public:
Base(int x):x(x){}
operator int (){ // 转换函数实现 Base 转 int
return x;
}
operator char(){
return char(x);
}
operator Demo(){ //Base 转 Demo 都是自定义类型转其他类型, 可用 自己设计的转换函数,但是如果是int * 转int 非自定义类型则要用标准内置转换函数
return x;
}
operator double(){
return double (x);
}
private:
int x;
};
int main()
{
int b = 65;
char c = 'B';
c = b; //内置转换函数(隐式转换)
cout << c << endl;
int a = 13 ;
Base obj(66);
a = obj; //自定义类型转其他类型,自定转换函数
cout << a << endl; //
char ch = obj ; //
cout << ch << endl;
Demo D = obj; // Demo D = Demo(obj);
double m = obj; //
cout << D << endl;
cout << m << endl;
return 0;
}
/*
cout:
A
66
B
66
*/
#include
using namespace std;
class Subclass; //前向声明 一个不完整的派生类
class Base{
public:
Base (int x):x(x){}
operator Subclass(); //转换函数的定义 必须放到派生类定义之后
private:
int x;
};
class Subclass:public Base{
public:
Subclass(int x ):x(x), Base (x){}
friend ostream &operator << (ostream &out, Subclass &obj)
{
out << obj.x;
return out;
}
private:
int x;
};
Base :: operator Subclass (){ //转换函数的实现
return x;
}
int main()
{
Subclass a(10);
Base b(6);
// b = a; //right: 向上隐式转换
a = b; // 向下隐式转换 本来是不允许,但是这里自己设计了转换函数可以实现向下转换
cout << a <
10.2、标准转换函数
#include
using namespace std;
class Base{
public:
//Base (int x):x(x){}
explicit Base (int x):x(x){} //限制隐式转换
private:
int x;
};
int main()
{
Base obj(1);
//但单参数的构造函数被 explicit 修饰,表示 该类 不能进行隐式转换,否则可以
//Base obj2 = 2; // 构造: Base obj2 = Base(2);
//obj = 1; //赋值
return 0;
}
#include
using namespace std;
class Base{
public:
Base(int x):x(x){}
private:
int x;
};
class Subclass:public Base{
public:
Subclass(int x):x(x), Base(x){}
private:
int x;
};
int main()
{
int a = 65;
int *p = &a;
cout << "*p:" << *p <(p);
//q = "hello";
cout << "q:" << q << endl;
cout << "*q:" << *q << endl;
int b = reinterpret_cast (p);
cout << "b:" << b << endl;
const char *str = "hello world";
//char *s = str; //error : str 常量指针 转普通指针
char *buf = const_cast (str);
// *buf = 'k'; //error:buf指向字符串常量区 不能修改
cout << "buf:" << buf << endl;
Base obj(6);
Subclass *s = static_cast(&obj); //static_cast实现 普通类型转换和具有继承关系的转换
//Subclass obj1 = static_cast(obj);
Subclass obj3(3);
Base obj2 = static_cast ( obj3 );
return 0;
}
/*
*p:65
p:0xbf99a24c
q:A
*q:A
b:-1080450484
buf:hello world
*/
10.3、自定义转换函数
#include
using namespace std;
class Demo{
public:
Demo () {cout << __LINE__<
10.4、慎用转换函数
十一、智能指针
11.1、什么是智能指针
11.2、shared-ptI
8#include <iostream>
using namespace std;
#include <memory> //加头文件
class Demo{
public:
Demo(){cout << __LINE__<<endl;}
~Demo(){cout << __LINE__<<endl;}
void fun(){cout << __LINE__<<endl;}
};
void test()
{
//定义一个shared_ptr 智能指针,此处有重载 * 运算符
shared_ptr<Demo> p (new Demo); //编译加 -std=c++0x
p->fun();
shared_ptr<Demo> pp = p; //pp 和 p 共享同一个地址
pp->fun();
#if 1//定义的两种方式
shared_ptr<Demo> q (new Demo) ;
shared_ptr<Demo> q2 = make_shared<Demo>(Demo()) ;
#endif
}
int main()
{
test();
cout<<"-------------"<<endl;
return 0;
}
11.3、unique ptr
#include <iostream>
using namespace std;
#include <memory> //加头文件
class Demo{
public:
Demo(){cout << __LINE__<<endl;}
~Demo(){cout << __LINE__<<endl;}
void fun(){cout << __LINE__<<endl;}
};
void test()
{
//定义一个unique_ptr 智能指针,资源独享 智能指针
unique_ptr<Demo> p (new Demo); //编译加 -std=c++0x
p->fun();
//unique_ptr<Demo> pp = p; //不允许pp 和 p 共享同一个地址(独享智能指针)
// pp->fun();
#if 0//定义的两种方式
unique_ptr<Demo> q (new Demo) ;
unique_ptr<Demo> q2 = make_unique <Demo> (Demo()) ;
#endif
}
int main()
{
test();
cout<<"-------------"<<endl;
return 0;
}
11.4、weak-ptr
十二、STL
12.1、STL简介
STL 即标准模板库
STL可分为六个部分:
容器( containers )
特殊的数据结构,实现了数组、链表、队列、等等,实质是模板类迭代器( iterators )
一种复杂的指针,可以通过其读写容器中的对象,实质是运算符重载算法( algorithms )
读写容器对象的逻辑算法:排序、遍历、查找、等等,实质是模板函数空间配置器( allocator)
容器的空间配置管理的模板类配接器( adapters )
用来修饰容器、仿函数、迭代器接口仿函数( functors )
类似函数,通过重载()运算符来模拟函数行为的类
12.2、标准容器简介
STL标准模版库是一种泛型编程。
12.3、vector
1