运算符重载是对已有运算符赋予多重含义
操作符重载的两种形式
要注意的是:
进行指针相等操作时,可能发生内存泄露;因为指针a=b,给a赋予b的值,相当于将a所指向地址改成b所指向地址,而a原来所指向地址发生丢失。
对于包含动态分配成员或者包含指针成员的类都应该提供拷贝构造函数;
在提供拷贝构造函数时,应考虑重载“=”赋值操作符号。
1.构造函数
2.析构函数
析构函数在对象的生存期即将结束的时刻被自动调用;
没有返回值;不接受任何参数;
如果不进行显式说明,系统将自动生成一个函数体为空的隐含析构函数。
一般来说,如果希望程序在对象被删除之前自动完成某些事情,可以将其写到析构函数中。
#include
using namespace std;
class A //抽象基类的声明;同时我们无法定义一个抽象基类的变量
{
public:
A(){ }; //注意构造函数的声明和定义都被需要
~A(){ };
virtual void display()=0;
};
class B:public A
{
public:
B(){ };
~B(){ };
void display()
{
cout<<"B::display()"<<endl;
}
};
class C:public A
{
public:
C(){ };
~C(){ };
void display()
{
cout<<"C::display()"<<endl;
}
} ;
int main()
{
B b;
C c;
b.display();
c.display();
return 0;
}
运行结果如下
说明了可以在不同派生类对象中定义名称相同、功能不同的虚函数(虚函数在基类中有声明)
1.一般虚函数成员
虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员函数实现时
运行过程中的多态需要满足的3个条件:
赋值兼容规则
声明虚函数
由成员函数来调用或者通过指针、引用来访问虚函数
虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定,而内联函数的处理是静态的
派生类的虚函数会覆盖基类的虚函数
2.虚析构函数
C++不可以声明虚构造函数,但可以声明虚析构函数
如下是未使用虚析构函数的程序
#include
using namespace std;
class Base{
public:
~Base();
};
Base::~Base(){
cout<<"Base destructor"<<endl;
}
class Derived:public Base{
public:
Derived();
~Derived();
private:
int *p;
};
Derived::Derived(){
p = new int(0);
}
Derived::~Derived(){
cout<<"Derived destructor"<<endl;
delete p;
}
void fun(Base *b){
delete b;
}
int main(){
Base *b= new Derived();
fun(b);
return 0;
}
运行时输出结果为:
Base destructor
这说明,通过基类指针删除派生类对象时调用的是基类的析构函数,派生类的析构函数未被执行,因此派生类对象中动态分配的内存空间没有得到释放,造成内存泄露;对于内存需求量较大,长期连续运行的程序来说,持续发生这样的错误是危险的,最终会因内存不足引起程序终止。
修正方法
class Base{
public:
virtual ~Base();
}
这时运行输出信息为
Derived destructor
Base destructor
说明派生类对象中动态申请的内存空间被正确释放,这是由于使用了虚析构函数,实现了多态。
3.纯虚函数和抽象类
纯虚函数
virtual 函数类型 函数名(参数表)=0;
//声明为纯虚函数之后,基类中可以不再给出函数的实现部分。
//纯虚函数的函数体由派生类给出
#include
using namespace std;
class A //抽象基类的声明;同时我们无法定义一个抽象基类的变量
{
public:
A(){ }; //注意构造函数的声明和定义都被需要
~A(){ };
virtual void display()=0;
};
class B:public A
{
public:
B(){ };
~B(){ };
void display()
{
cout<<"B::display()"<<endl;
}
};
class C:public A
{
public:
C(){ };
~C(){ };
void display()
{
cout<<"C::display()"<<endl;
}
} ;
int main()
{
B b;
C c;
b.display();
c.display();
return 0;
}
运行结果如下
说明了可以在不同派生类对象中定义名称相同、功能不同的虚函数(虚函数在基类中有声明)
#include
using namespace std;
class A //抽象基类的声明;同时我们无法定义一个抽象基类的变量
{
public:
A(){ }; //注意构造函数的声明和定义都被需要
~A(){ };
virtual void display()=0;
};
class B:public A
{
public:
B(){ };
~B(){ };
void display()
{
cout<<"B::display()"<<endl;
}
};
class C:public A
{
public:
C(){ };
~C(){ };
void display()
{
cout<<"C::display()"<<endl;
}
} ;
int main()
{
B b;
C c;
b.display();
c.display();
return 0;
}
运行结果如下
说明了可以在不同派生类对象中定义名称相同、功能不同的虚函数(虚函数在基类中有声明)
函数的形参和局部变量,可以用栈来存储,叫做运行栈;后进先出;
运行栈中的数据分为一个一个的栈帧,每个栈帧对应一次函数调用,包括这次函数调用时的形参值、局部变量值、临时数据等。
函数调用需要消耗一定内存资源和运行时间来传递参数和返回值,要记录调用时的状态,以便保证调用完成后能够正确地返回并继续运行;如果有的函数成员需要被频繁调用,而且代码比较简单,可以被定义为内联函数。
隐式声明;
显式声明:
inline void Clock::showTime()
联合体:
联合体的各个对象成员,不能有自定义的构造函数、自定义的析构函数和重载的赋值赋值运算符;
联合体不能继承,不支持多态;
操作数栈 ODS
操作符栈 OPS
1.浅复制与深复制
int main()
{
Array <int> a(10);
...
Array <int> b(a);
//如上浅复制,只是将对象a中数组元素地址简单赋值给对象b的指针成员,没有复制数组元素,对象b没有构造自己的数组。结果是a和b共同使用同一段内存空间存放数组元素,这样会造成混乱。
Array类的应用
求范围2~n中的质数,n在程序运行时由键盘输入
#include
#include
#include"Array.h"
using namespace std;
int main()
{
Array<int> a(10); //用来存放质数的数组,初始状态有10个元素
int count=0;
int n;
cout<<"Enter a value>=2 as upper limit for prime numbers:";
cin>>n;
for(int i=2;i<=n;i++)
{ 检查i是否能被比它小的质数整除,
bool isPrime=true;
for(int j=0;j<cout;j++)
if(i%a[j]==0)
{
isPrime=false;
break;
}
//把i写入质数表
if(isPrime)
{
if(count==a.getSize())
a.resize(count*2);
a[count++]=i;
}
}
for(int i=0;i<count;i++) //输出质数
cout<<setw(8)<<a[i];
cout<<endl;
return 0;
}
栈的应用----- 一个简单的整数计算器
主要思想:每当遇到操作数,便入栈;遇到操作符,便连续弹出两个操作数并执行运算,并将运算结果压入栈顶。输入“c”时,清空操作数栈;输入“q”时,清空操作数栈并结束程序
//Calculator.h
#ifdef CALCULATOR_H
#define CALCULATOR_H
#include"Stack.h"
class Calculator{
private:
Stack<double>s; //操作数栈
void enter(double num); //将num压入栈
bool getTwoOperands(double &opnd1,double &opnd2);
void compute(char op);
public:
void run();
void clear();
};
#endif
//Calculator.cpp
#include"Calcluator.h"
#include
#include
#include
using namespace std;
void Calculator::enter(double num) //将操作数压入栈中
{ s.puch(num); }
//连续将两个操作数弹出栈,放在opnd1和opnd2中
//如果栈中没有两个操作数,则返回false并输出相关信息
bool Calculator::getTwoOperands(double &opnd1, double &opnd2)
{
if(s.isEmpty()) //检查栈是否为空
{
cerr<<"Missing operand!"<<endl;
return false;
}
opnd1=s.pop(); //将右操作数弹出栈
if(s.isEmpty()) //检查栈是否为空
{
cerr<<"Missing operand!"<<endl;
return false;
}
opnd2=s.pop(); //将左操作数弹出栈
return true;
}
void Calculator::compute(char op) //执行运算
{
double operand1,operand2;
bool result=getTwoOperands(operand1,operand2);
if(result) //如果取数成功
{
switch(op)
{
case'+':
s.push(operand2+operand1);
break;
case'-':
s.push(operand2-operand1);
break;
case'*':
s.push(operand2*operand1);
break;
case'/':
if(operand1==0)
{
cerr<<"Divided by 0!" <<endl;
s.clear(); //除数为0时清空栈
}
else
s.push(operand2/operand1);
break;
case'^':
s.push(pow(operand2,operand1));
break;
default:
cerr<<"Unrecognized operator!"<<endl;
break;
}
cout<<"="<<s.peek()<<" ";
}
else
s.clear(); //操作数不够,清空栈
}
//工具函数,将字符串转换为实数;
inline double stringToDouble(const string &str)
{
istringstream stream(str); //字符串输入流
double result;
stream>>result;
return result;'
}
void Calculator::run() //读入并处理后缀表达式
{
string str;
while(cin>>str,str!="q"){
switch(str[0]){
case'c':
s.clear();break;
case'-':
if(str.size()>1)
enter(stringToDouble(str));
else
compute(str[0]);
break;
case'+': //遇到其他操作符时
case'*':
case'/':
case'^':
compute(str[0]); //执行计算
break;
default:
enter(stringToDouble(str));
break;
}
}
}
void Calculator::clear(){ //清空操作数栈
s.clear();
}
//9_9.cpp
#include"Calculator.h"
int main(){
Calculator c;
c.run();
return 0;
}
1.容器(7种)
顺序容器:向量,双端队列,列表容器
关联容器:集合,多重集合,映射,多重映射
#include
#include
#include
#include
#include
using namespace std;
int square(int x)
{
return x*x;
}
int main(){
const int N=5;
vector<int> s(N);
for(int i=0;i<N;i++)
cin>>s[i];
transform(s.begin(),s.end(),ostream_iterator<int>(cout," "),square);
cout<<endl;
return 0;
}
#include
#include
#include
#include
#include
using namespace std;
int main(){
const int N=5;
vector<int> s(N);
for(int i=0;i<N;i++)
cin>>s[i];
transform(s.begin(),s.end(),ostream_iterator<int>(cout," "),negate<int>());
cout<<endl;
return 0;
}
结果如下
1 2 -3 4 5
-1 -2 3 -4 -5