c++学习笔记.docx

上传人:b****4 文档编号:27118695 上传时间:2023-06-27 格式:DOCX 页数:43 大小:442.95KB
下载 相关 举报
c++学习笔记.docx_第1页
第1页 / 共43页
c++学习笔记.docx_第2页
第2页 / 共43页
c++学习笔记.docx_第3页
第3页 / 共43页
c++学习笔记.docx_第4页
第4页 / 共43页
c++学习笔记.docx_第5页
第5页 / 共43页
点击查看更多>>
下载资源
资源描述

c++学习笔记.docx

《c++学习笔记.docx》由会员分享,可在线阅读,更多相关《c++学习笔记.docx(43页珍藏版)》请在冰豆网上搜索。

c++学习笔记.docx

c++学习笔记

函数对象

1.函数对象的定义

//声明一个普通的类并重载“()”操作符:

classNegate 

{

public:

 

intoperator()(intn){return-n;} 

};

   重载操作语句中,记住第一个圆括弧总是空的,因为它代表重载的操作符名;第二个圆括弧是参数列表。

一般在重载操作符时,参数数量是固定的,而重载“()”操作符时有所不同,它可以有任意多个参数。

2.使用函数对象

#include

usingstd:

:

cout;

voidCallback(intn,Negate&neg) 

{

intval=neg(n);//调用重载的操作符“()” 

cout<

}

//注意neg是对象,而不是函数。

编译时,编译器将语句

intval=neg(n);转化为intval=neg.operator()(n);

通常,函数对象不定义构造函数和析构函数。

因此,在创建和销毁过程中就不会发生任何问题。

前面曾提到过,编译器能内联重载的操作符代码,所以就避免了与函数调用相关的运行时问题。

//为了完成上面个例子,我们用主函数main()实现Callback()的参数传递:

intmain() 

{

Callback(5,Negate());//输出-5

}

本例传递整数5和一个临时Negate对象到Callback(),然后程序输出-5。

3.编写模板函数对象

从上面的例子中可以看出,其数据类型被限制在int,而通用性是函数对象的优势之一,如何创建具有通用性的函数对象呢?

方法是使用模板,也就是将重载的操作符“()”定义为类成员模板,以便函数对象适用于任何数据类型:

如double,_int64

classGenericNegate

{

public:

 

templateToperator()(Tt)const{return-t;}

};

intmain()

{

GenericNegatenegate;

cout<

cout<

}

4.标准库中函数对象

C++标准库定义了几个有用的函数对象,它们可以被放到STL算法中。

例如,sort()算法以

判断对象(predicateobject)作为其第三个参数。

判断对象是一个返回Boolean型结果的

模板化的函数对象。

可以向sort()传递greater<>或者less<>来强行实现排序的升序或降序:

#include//forgreater<>andless<>

#include//forsort() 

函数对象用于求最大值

#include

#include

#include

usingnamespacestd;

template

constObject&findMax(constvector&arr,Comparatorcmp)//求最大值

{

intmaxIndex=0;

for(inti=1;i

if(cmp.isLessThan(arr[maxIndex],arr[i]))//此时的isLessThan还不知道具体的定义,要看cmp到底是什么

maxIndex=i;//maxIndex每次取到的都是i值,而且只有当arr[maxIndex]

returnarr[maxIndex];

}

classCaseInsensitiveCompare

{

public:

//true:

lhs

lhs>rhs

boolisLessThan(conststring&lhs,conststring&rhs)const

{

return_stricmp(lhs.c_str(),rhs.c_str())<0;//使用string的成员函数,不区分大小写

//returnlhs

}

};

intmain()

{

vectorarr(4);

arr[0]="ZEBRA";

arr[1]="zebrc";

arr[2]="zebrf";

arr[3]="zebrd";

cout<

return0;

}

虚函数表

基类的析构函数为什么要设为Virtual

未初始化的对象指针调用虚函数时会导致错误

Father*pf=NULL;//未初始化的对象指针调用虚函数时会导致错误

pf->vir();//运行错误

虚函数实现多态

#include

usingnamespacestd;

classFather

{

public:

virtualvoidvir()

{

cout<<"父类的vir()函数被执行"<

}

};

classSon:

publicFather

{

public:

voidvir()

{

cout<<"子类的vir()函数被执行"<

}

};

intmain()

{

Fatherf;

Sons;

f.vir();//父类的vir()函数被执行

s.vir();//子类的vir()函数被执行

Father*pf;

pf=newFather;//父类对象赋值给父类指针,也可以通过&f赋值

pf->vir();//父类的vir()函数被执行

pf=newSon;//子类对象赋值给父类指针,也可以通过&s赋值

pf->vir();//子类的vir()函数被执行

return0;

}

1.子类对象可以赋值给父类指针(可以通过取地址或者new赋值)

2.父类对象不可以赋值给子类指针,因为子类指针很可能访问到父类没有的一些属性及函数,会出错

3.对于纯虚基类,不能创建对象,也不能使用new,只能创建指针,但只能将子类对象赋值给这个基类指针;因而纯虚基类的指针也只能访问子类的同名虚函数。

C++中不能重载为友元函数的四个运算符

C++规定有四个运算符=,->,[],()不能重载为友员函数,只能重载为类的非静态成员函数

初始化和赋值的区别

拷贝构造函数和赋值构造函数

1.初始化:

对于一个类Aa对象,初始化发生在定义a时,比如Aa(7)或Aa=7;都叫初始化,都是调用自己定义的构造函数

2.赋值:

给已经存在了的对象赋值,如Aa;a=7;

3.拷贝构造函数首先是一个构造函数,同样没有返回值,也是用来构造一个对象的,只不过参数是一个类对象,即用一个对象构造一个新对象

4.赋值构造函数是把一个对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是的话就不做任何操作。

而且需要返回一个对象自身的引用(这必定要调用拷贝构造函数),以便赋值之后的操作 

下面分两种情况讨论一段代码

a.重载了赋值运算符

#include

usingnamespacestd;

classA

{

public:

A()

{

x=99;cout<<"CallA()"<

}

A(intxx)

{

cout<<"CallA(intxx)"<

x=xx;

}

A(A&a)

{

x=a.x;

cout<<"拷贝构造函数被调用"<

}

Aoperator=(Aa)//赋值构造函数被调用

{

x=a.x;

cout<<"赋值构造函数被调用"<

return*this;

}

Aoperator=(intxx)//重载赋值运算符

{

cout<<"重载赋值运算符被调用"<

x=xx;

return*this;

}

private:

intx;

};

intmain()

{

Aa;//调用默认构造函数

a=7;//由于重载了上述赋值运算符,故调用赋值运算符,在赋值运算符内又调用了拷贝构造函数

Ab(a);//调用拷贝构造函数

system("pause");

return0;

}

输出结果:

b.没有重载赋值运算符

#include

usingnamespacestd;

classA

{

public:

A()

{

x=99;cout<<"CallA()"<

}

A(intxx)

{

cout<<"CallA(intxx)"<

x=xx;

}

A(A&a)

{

x=a.x;

cout<<"拷贝构造函数被调用"<

}

Aoperator=(Aa)//赋值构造函数被调用

{

x=a.x;

cout<<"赋值构造函数被调用"<

return*this;

}

private:

intx;

};

intmain()

{

Aa;//调用默认构造函数

a=7;//由于没有重载了上述赋值运算符,故先调用构造函数创建一个临时对象,再调用赋值构造函数,在赋值构造函数内又调用了拷贝构造函数

Ab(a);//调用拷贝构造函数

system("pause");

return0;

}

输出结果:

c++中#include中函数列表

using:

:

abs;//绝对值

using:

:

acos;//反余弦

using:

:

acosf;//反余弦

using:

:

acosl;//反余弦

using:

:

asin;//反正弦

using:

:

asinf;//反正弦

using:

:

asinl;//反正弦

using:

:

atan;//反正切

using:

:

atan2;//y/x的反正切

using:

:

atan2f;//y/x的反正切

using:

:

atan2l;//y/x的反正切

using:

:

atanf;//反正切

using:

:

atanl;//反正切

using:

:

ceil;//上取整

using:

:

ceilf;//上取整

using:

:

ceill;//上取整

using:

:

cos;//余弦

using:

:

cosf;//余弦

using:

:

cosh;//双曲余弦

using:

:

coshf;//双曲余弦

using:

:

coshl;//双曲余弦

using:

:

cosl;//余弦

using:

:

exp;//指数值

using:

:

expf;//指数值

using:

:

expl;//指数值

using:

:

fabs;//绝对值

using:

:

fabsf;//绝对值

using:

:

fabsl;//绝对值

using:

:

floor;//下取整

using:

:

floorf;//下取整

using:

:

floorl;//下取整

using:

:

fmod;//求余

using:

:

fmodf;//求余

using:

:

fmodl;//求余

using:

:

frexp;//返回value=x*2n中x的值,n存贮在eptr中

using:

:

frexpf;//返回value=x*2n中x的值,n存贮在eptr中

using:

:

frexpl;//返回value=x*2n中x的值,n存贮在eptr中

using:

:

ldexp;//返回value*2exp的值

using:

:

ldexpf;//返回value*2exp的值

using:

:

ldexpl;//返回value*2exp的值

using:

:

log;//对数

using:

:

log10;//对数

using:

:

log10f;//对数

using:

:

log10l;//对数

using:

:

logf;//对数

using:

:

logl;//对数

using:

:

modf;//将双精度数value分解成尾数和阶

using:

:

modff;//将双精度数value分解成尾数和阶

using:

:

modfl;//将双精度数value分解成尾数和阶

using:

:

pow;//计算幂

using:

:

powf;//计算幂

using:

:

powl;//计算幂

using:

:

sin;//正弦

using:

:

sinf;//正弦

using:

:

sinh;//双曲正弦

using:

:

sinhf;//双曲正弦

using:

:

sinhl;//双曲正弦

using:

:

sinl;//正弦

using:

:

sqrt;//开方

using:

:

sqrtf;//开方

using:

:

sqrtl;//开方

using:

:

tan;//正切

using:

:

tanf;//正切

using:

:

tanh;//双曲正切

using:

:

tanhf;//双曲正切

using:

:

tanhl;//双曲正切

using:

:

tanl;//正切

intabs(inti);//处理int类型的取绝对值

doublefabs(doublei);//处理double类型的取绝对值

floatfabsf(floati);/处理float类型的取绝对值

C++运算符重载

1.怎么实现运算符的重载?

方式:

类的成员函数或友元函数(类外的普通函数)

规则:

不能重载的运算符有. 和.*和?

:

和:

:

 和sizeof

友元函数和成员函数的使用场合:

一般情况下,建议一元运算符使用成员函数,二元运算符使用友元函数

如果++作为后缀使用,则需要一个额外的参数,用于和前缀区分开

一元运算符

作为成员函数,0个参数;作为友元函数,1个参数

二元运算符

作为成员函数,1个参数;作为友元函数,2个参数

       1、运算符的操作需要修改类对象的状态,则使用成员函数。

如需要做左值操作数的运算符(如=,+=,++)

        2、运算时,有数和对象的混合运算时,必须使用友元

       3、二元运算符中,第一个操作数为非对象时,必须使用友元函数。

如输入输出运算符<<和>>

具体规则如下:

运算符

建议使用

所有一元运算符

成员函数

=()[] ->

必须是成员函数

+=-=/=*=^=&=!

=

%=>>=<<=,似乎带等号的都在这里了.

成员函数

所有其它二元运算符,例如:

–,+,*,/,%

友元函数

<<>>

必须是友元函数

2.->运算符的重载

->操作符看起来像二元操作符:

接受一个对象和一个成员名。

但是,其实箭头操作符是一元操作符。

况且->的右操作数不是表达式。

如何开始:

由a开始,分下面两种情况

1、a是指针,那么就是我们熟悉的,指向我们a类型的成员数据或函数“b”;到这里,就结束了!

2、a是对象,那么a必须有成员函数"operator->"(否则会报错)。

那么就调用a的operator->函数。

由于operator->返回的可能是指针,也可能是对象,那么就又进入了下一轮递归:

是指针还是对象,走1或2,如此递归。

如何结束:

不管a是指针或者对象,最终都是走到指针结束(当然,写错了也走不下去)。

代码

#include

usingnamespacestd;

classA

{

public:

voidaction()

{

cout<<"ActioninclassA!

"<

}

};

classB

{

Aa;

public:

A*operator->()

{

return&a;

}

voidaction()

{

cout<<"ActioninclassB!

"<

}

};

classC

{

Bb;

public:

Boperator->()

{

returnb;

}

voidaction()

{

cout<<"ActioninclassC!

"<

}

};

intmain(intargc,char*argv[])

{

C*pc=newC;

pc->action();

Cc;

c->action();

getchar();

return0;

}

代码分析

 C*pc=newC;

 pc->action();

输出的结果是

ActioninclassC!

这个结果比较好理解,pc是类对象指针,此时的箭头操作符使用的是内置含义,故pc直接调用其成员函数action。

 而下面的代码

 Cc;

 c->action();

输出的结果是

ActioninclassA!

 其实c->action()的含义与c.operator->().operator->()->action();相同。

c是对象,c后面的箭头操作符使用的是重载箭头操作符,即调用类C的operator->()成员函数,此时返回的是类B的对象。

所以接着调用类B的operator->()成员函数,此时返回的是类A的指针。

最终由A的指针调用A的action()成员函数。

注释:

这里存在一个递归调用operator->()的过程,直到最后使用了一次内置含义的箭头操作符。

参考博客:

两个整数相除保留两位小数

int a = 100; 

int b = 3; 

float c = (float)a / (float)b; 

String aa = FormatFloat("0.00",c); 

c语言二维数组a【0】与a与a【0】【0】有什么区别?

数组在内存中是连续按行分布的,对于a[2][3]={{1,2,3},{4,5,6}};它在内存中的分布式1,2,3,4,5,6;所以他等价于a[2][3]={1,2,3,4,5,6};在c/c++中,数组名也即是数组首地址,这里加入a=0x1000;那么它的地址分就是:

1000,1004,1008,100c,1010,1014;&a[0][0]也就取第一个元素的地址,即1000,a[0]是第一行1000,1004,1008的首地址,也是1000,也即是a元素的首地址;

int*b=a[0];

*b和a[0]是等的,是a数组第一行首地址;不同的是a[0]是只读的,*b是可读写的因为b是指针变量,你可以试试a[0][3],a[0][4],a[0][5]都能打印出来,别看a[2][3]以为这样越界了,其实越不越界是按地址来算的,地址上讲,&a[0][3]=a[1],所以完全没问题;按照地址计算上面打印的值就是4,5,6;同理用*(b+3),*(b+4),*(b+5)也是一样的道理;

a就是数组的首地址,a[0]是第一行第一个元素的首地址,也即是a的首地址,&a[0]是第一行的首地址,从数值上讲,没问题,只是数据类型上不一样;

a[0][0]就是取第一行第一个元素的值,也即是1

int变量转换成二进制

#include

#include

usingnamespacestd;

inta=-2;

intb=4;

//cout<>(a)<

cout<<(a&b)<

Char*指针与char数组

#include

#include

usingnamespacestd;

intmain()

{

chara[6]="hello";

cout<(a)<

cout<>((int)(a))<

cout<<(void*)(a+1)<

cout<>((int)(a+1))<

char*p=NULL;

p=a;

cout<

strings="hello";

cout<<(void*)&s[0]<

cout<<(void*)&s[1]<

return0;

}

注意:

1、&a表示的是指针变量a的地址,a表示的是数组的首地址,两者数值上相同。

2、用cout输出a,要通过(void*)进行转换,转换之后输出的是16进制的地址;

用(int)转换,输出的是十进制数值。

一个字符如’a’,占一个字节,由8位二进制组成。

指针每自增1,由于字符只占一个字节,所以指针在寻址的时候就是一个字节的寻址,数值上只增加1

00010100是一

展开阅读全文
相关搜索

当前位置:首页 > 农林牧渔 > 林学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1