C++数据结构小记

1、类模板的声明
template<class genType>
class genclass{
genType storage[50];
…………….
}
在此之后可决定初始化genType
genClass intObject;
genClass floatObject;

进一步,将数组大小推迟到对象定义阶段
templat <class genType,int size=50>
class genClass{
gentype storage[size];
………….
}
可定义以下对象
genClass<int> intObject1;
genClass<int,100> intObject2;
genclass<float,100> floatObject1;

2.模板函数定义如下
template<class genType>
void swap(genType& el1,genType& el2){
genType tmp=el1;
el1=el2;
el2=tmp;
}
可进行如下定义:
swap(n,m); //swap two integers;
swap(x,y); //swap two floats;

3.C与C++的指针定义
int *P;
P=new int;
delete P; //删除P指向的存储空间
P=0; //将指向存储空间的地址名变为0,表示P变为空

int *p;
p=new int[n];
delete [] p;
p=0;

int* a=(int*)malloc(5*sizeof(char));
free a;
a=0;

4.指针与复制构造函数
将数据从一个对象复制到另一个对象时,若指针数据成员未被正确处理,会产生一些问题。考虑下面定义:
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strduo(n);
age=a;
}
}
如下声明
Node node1(“Roger”,20),node2(node1); //or node2=node1;
然而经过下面赋值:
strcpy(node2.name,”Wendy”);
node2.age=30;

输出语句
cout<<node1.name<<’ ‘<<node1.age<<’ ‘<<node2.name<<’ ‘<<node2.age;
结果为:
Wendy 20 Wendy 30
从中发现对node2的复制影响了node1,主要原因是缺少复制构造函数,node1和node2的指针指向了同一内容
定义拷贝构造函数如下:
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strduo(n);
age=a;
}
Node(const Node &n){
name=strdup(n.name);
age=n.age;
}
}
使用新的构造函数,声明node2(node1)生成了“Roger”的另一个副本(进行常量引用时,编译器会实现生成临时变量的作用机制),node2.name指向该副本(即临时变量)。当执行赋值:
strcpy(node2.name,”Wendy”);
node2.age=30;
后,对象node1保持不变

同时赋值运算符会引起同样的问题,如果未提供赋值运算符的定义,下面的赋值操作
node1=node2;
因此必须重载赋值运算符
struct Node{
char* name;
int age;
Node(char *n=” “,int a=0){
name=strduo(n);
age=a;
}
Node(const Node &n){
name=strdup(n.name);
age=n.age;
}
Node & operator=(const Node& n){
if(this!=&n){
if(name!=0)
free name;
name=strdup(n.name);
age=n.age;
}
}
}

5.重载运算符基本概念
1)重载运算符是具有特殊名字的函数,名字由关键字operator与其后要定义的运算符号组成,重载运算符也包括返回类型、参数列表以及函数体
2)重载运算符函数参数数量与该运算符作用的运算对象数量一样多,例一元运算符有1个参数,二元运算符有2个参数
3)若运算符函数是类的成员函数,则第一个(左侧)运算对象绑定到隐式this指针,因此成员运算符函数参数数量比运算符函数参数数量少1个,举例:
//一个非成员运算符函数的等价调用
data1+data2;
operator+(data1,data2);

//成员运算符函数
data1 +=data2;
data1.operator+=(data2);
两句都调用了成员函数operator+=,将this绑定到data1地址,将data2作为实参传入参数。
非成员与成员的定义方式如下:
非成员,例如重载”+”
Sales_data operator+(const Sales_data &lhs,const Sales_data &rhs)
{
Sales_data sum;
sum.data=lhs.data+rhs.data;
return sum;
}
成员,例如“=”
Node & Node::operator=(const Node& n){
if(this!=&n){
if(name!=0)
free name;
name=strdup(n.name);
age=n.age;
}
4)对于运算符函数来说,它或者是类的成员,或者至少含有一个类类型参数

6.const的引用
对常量的引用不能被用作修改它所绑定的对象:
const int ci=1024;
const int &r1=ci;
r1=42 //错误:r1是对常量的引用
int &r2=ci //错误:试图让非常量引用指向一个常量对象

引用类型必须与其所引用对象类型一致,允许一个常量引用绑定非常量的对象、字面值甚至一般表达式
int i=42;
const int &r1=i;
const int &r2=42;
const int &r3=r1*2;

对const的引用是一个并非const的对象
int i=42;
int &r1=i;
const int &r2=i; //r2也绑定对象i,但不允许通过r2修改i的值
r1=0; //r1并非常量,i的值修改为0
r2=0; //错误:r2是一个常量引用
int &r4=42; //错误:不能用字面值去初始化一个非常量引用

double dval=3.14;
const int &ri=dval;
编译器把上述代码变成如下过程:
const int temp=dval; //由双精度浮点数生成一个临时的整型常量
const int &ri=temp; //让ri绑定这个临时量

尽量使用常量引用
1.将函数不会改变的形参定义成普通引用,会导致函数可以修改它的实参的值
2.使用普通引用会极大限制函数所能接受的实参类型。我们不能把const对象、字面值或者需要类型转换的对象传递给普通引用形参

7.函数指针
//声明一个pf的函数,返回指针bool*
bool *pf(const string&,const string&);

//pf指向一个函数,是函数指针
bool lengthCompare(const string&,const string&);
bool (*pf)(const string&,const string&);

使用函数指针
当将一个函数名作为一个值使用时,该函数自动转换为指针。例如,将lengthCompare的地址赋给pf:
pf=lengthCompare; //pf指向名为lengthCompare的函数
pf=&lengthCompare; //等价赋值语句,取地址符是可选的

此外,可以直接使用指向函数的指针调用该函数
bool b1=pf(“hello”,”goodbye”); //调用lengthCompare函数
bool b2=(*pf)(“hello”,”goodbye”); //一个等价调用

指向不同函数类型的指针不存在转换规则,可以为指针赋一个nullptr或者值为0,表示指针没指向任何一个函数:
string::size_type sumlength(const string&,const string&);
bool cstringCompare(const char*,const char*);
pf=0; //正确:pf不指向任何函数
pf=sumLength; //错误:返回类型不匹配
pf=cstringCompare; //错误:形参类型不匹配

重载函数的指针
void ff(int*);
void ff(unsigned int);

void (*pf1)(unsigned int)=ff; //pf1指向ff(unsigned int)

函数指针形参
函数的形参看起来像函数类型,实际是当成指针使用。当直接把函数当做实参使用时,会自动转化为指针,如

//第三个形参是函数类型,会自动转换成指向函数的指针
void useBigger(const string& s1,const string &s2,bool pf(const string&,const string&));
//等价声明,显示定义函数指针
void useBigger(const string& s1,const string &s2,bool (*pf)(const string&,const string&));

通过类型别名简化使用函数函数指针代码
typedef bool Func(const string&,const string&); //Func是函数类型
typedef bool (*FuncP)(const string&,const string&); //FuncP是函数指针

因此重新声明useBigger
void useBigger(const string& s1,const string &s2,Func);
void useBigger(const string& s1,const string &s2,FuncP);

返回指向函数类型的指针
和函数类型的形参不同,返回类型不会自动转换为指针,需要显示的将返回类型指定为指针。
using F=int(int*,int); //F是函数类型,不是指针
using pF=int()(int,int); //pF是函数指针

pF f1(int); //正确:pF是指向函数的指针,f1是返回指向函数的指针
F* f1(int); //正确:显示指定返回类型是指向函数的指针
F f1(int); //错误:F是函数类型,f1不能返回一个函数
int (f1(int))(int,int); //直接声明f1

8.多态性
class Class1{
public:
virtual void f(){
cout<<”Function f() in Class1\n”;
}
void g(){
cout<<”Function g() in Class1\n”;
}
};

class Class2{
public:
virtual void f(){
cout<<”Function f() in Class2\n”;
}
void g(){
cout<<”Function g() in Class2\n”;
}
};

class Class3{
public:
virtual void h(){
cout<<”Function h() in Class3\n”;
}
};

int main()
{
Class1 object1,*p;
p=new Class1;
Class2 object2;
Class3 object3;
p->f();
p->g();
p=(Class1*) &object2;
p->f();
p->g();
p=(Class1*) &object3;
p->f(); //出错:找不到Class3中的f()
p->g();
p->h(); //出错:Class1中不存在h()
return 0;
}
程序输出如下:
Function f() in Class1;
Function g() in Class1;
Function f() in Class2;
Function g() in Class1;
Function g() in Class1;

动态绑定与静态绑定
1.静态绑定
对于静态绑定,调用哪个函数是在编译阶段确定。在上面例子中,指针p声明为Class1*类型,因此,若p指向非虚函数g(),则不管在程序什么地方执行p->g(),都会调用Class1中定义的g()。这是因为,编译器根据p的类型声明做出决定,且g()不是虚函数。

2.动态绑定
对于动态绑定,调用哪个函数要到程序运行阶段才能确定。在C++中,动态绑定是通过将成员函数声明为virtual来实现的。在这种方式中,若对虚函数成员进行调用,则选择哪个函数不依赖于声明的指针类型,而依赖于指针当前指向的类型
当一个成员为virtual类型,系统就检查当前指针类型,调用正确函数成员。最初将p声明为Class1*类型,就会调用属于Class1的虚函数f(),而当将类型为Class2的对象object2的地址赋给p后,就会调用属于Class2的f()。(Class2的f()是否为虚函数都一样调用,只要f()的声明形式与Class1中相同

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值