我的C++学习笔记.docx
《我的C++学习笔记.docx》由会员分享,可在线阅读,更多相关《我的C++学习笔记.docx(13页珍藏版)》请在冰豆网上搜索。
我的C++学习笔记
1.结构体struct和类class可以互换,本质结构体是一种特殊的类,struct默认是public而class默认是private
2.Consinta;常量是要进行初始的。
:
a
(1);
3.引用必须在定义的时候初始化。
4.全局变量或者全局对象,在进入main函数之前就已经分配好了内存空间
5.当基类的构造函数是为缺省的值时,在派生子类实例化时基类构造可以不带参数。
6.:
:
Showwindow()这样的前面加两个冒号,表示调用的是全局函数,即平台sdk的函数。
7.用const结尾的成员函数只能调用其他const结尾的成员函数。
因为其他函数并不能保证同样不修改类的成员变量
成员函数后加const,表示:
类作者提示你,这个函数不会导致对象状态改变
可以理解为"表示这个函数不会修改任何成员变量"
但不可绝对化。
其实,还是可以修改由mutable关键字修饰的成员变量的。
8.C++中,构造函数和析构函数可以被显示调用.显示调用默认构造函数的语法:
a.A:
:
A();(不能写成a.A();),显示调用非默认构造函数的语法:
a.A:
:
A(7);(不能写成a.A(7););显示调用析构函数的语法:
a.A:
:
~A();(可以写成a.~A();).
显示调用构造函数和析构函数就像调用一般的函数一样,并不意味着创建或销毁对象;
如果构造函数中动态分配了空间,则显示调用构造函数会造成内存泄露.创建对象时的隐式构造函数调用已经为对象分配了动态内存.当用创建好的对象显示调用构造函数时,对象指向的动态内存更新显示调用时所分配的,对象生命周期结束时析构函数所释放掉的是后一次分配的动态内存,也就是说创建对象时隐式构造函数调用所分配的那块内存泄漏了.
如果析构函数中释放动态分配的空间,则会造成多次释放同一内存,会出现严重错误.
9.析构函数和内存释放,有一篇的总结,因为内容长,以后贴上来
先看个简易的
classA
{
public:
A(inti=3){a=i;};
~A(){cout<<"destructor"< intvalue(){returna;}
private:
inta;
};
intmain()
{
Aa;
A*p=new(&a)A(10);
cout< deletep;
return0;
}
在a对象的地址上未析构a的时候,创建了新的无名堆对象,这样a的数据被覆盖了,
但是编译器并未收到析构函数调用的消息,所以它认为那块内存存放的仍然是a对象的数据,所以a.value()仍可以取回值,取回当时内存中那个地址处的值,这是已经是10了。
然后deletep的时候,执行两步工作,
1。
无名对象的析构函数调用,告诉编译器,那块内存中的值不能解释成无名对象的数据了,对于无名对象而言,已经无效了(但对于a对象而言,因为编译器并未收到a对象析构函数的调用,所以认为那块内存中的数据对于a而言仍是有效的,是a的数据的)
2。
问题发生在这个时候,deletep的第二个动作,归还内存给系统,标识为空闲内存(没有任何对象引用到的,没有任何函数占用的内存),而事实上,这是对于p所指对象而言,对于a对象,它仍引用到这块内存,这样产生了异常,系统认为是快空闲内存了,但确有个对象引用到了这块内存,行为属未定义。
这就好像两个指针指向同一块的内存,一个把内存释放了,另一个指针成了悬垂指针,对悬垂指针解引用取出的值无意义,随机的,看当时内存中的数据
简单的说,
构造函数就是通知编译器,这块内存的数据解释成一个对象,哪块是哪个数据成员的数据,这些信息是构造函数给编译器的,都是隐式给的;显式的动作可以放在构造函数中(并未必需的)的,是初始化(不给初始化值可以的,随机值),获取资源
析构函数就是通知编译器,这块内存中的数据以后无需维护了,可以让其它使用了,里面的数据再不能解释成类对象的数据了,析构函数调用之后可以两种选择
1。
一般情况,归还内存,栈的,堆的,都是
2。
保留内存,只析构对象,这常见于placementnew,delete往往同时执行了两个动作,所以这是候并不能直接delete,而是显式调用析构函数(详细论述待以后的总结)
10.学习笔记之c++
11.int*pia=newint[10];
我们并不能给动态分配的数组每个元素一个初始化值,语法并不支持
基于这个原因,对于对象数组
如果并没有默认构造函数,不能创建动态对象数组,因为new对象数组的时候,自动调用构造函数以建立对象,(new不只是分配内存而已,而且给编译器标识出这块内存存放的是什么类型的对象,int,classA,这也属于new的行为,所以它调用构造函数,以提供识别数据类型,解析二进制数据流的能力)如果并没有默认构造函数,
语法上不支持逐个初始化列表给动态数组,这样就没法构造对象了
容器vector和内置数组类型,是一个道理,
关键在于理解:
new的动作不只是分配内存,而且进行类型标识的行为
12.两者释放的资源是不一样的,delete释放p指向的内容,它也调用析构函数,也就是说,后者包含前者
而析构函数不释放p指向的内容,而是p指向的对象的成员变量占用的资源(不一定是内存,还可能是其他资源,如文件句柄/窗口/socket等)
13.类的静态成员与常成员
14.Const
15.1const与volatile的用法
1const
#include
#include
//行参数指向const类型变量的指针
voiddisplay_c(consint*pi)
{
cout<<"display_c:
"<<*pi< }
//行参为普通类型变量的指针
voiddisplay(int*pi)
{
cout<<"display_c:
"<<*pi< }
//const类型变量
constinti1=1;
//errori1=3;
//const类型变量的指针
inti2=2;
constint*pi2=&i2;
//error*pi2=3
//const指针
int*constpi3=&i3;
*pi3=5;
//error*pi3=&i4可以赋予不同的值,但是不可一指向不同的变量
//指向const内容的const指针
constint*cosntpi5=&i5;
2sizeof返回某个便赖宁嘎或数据类型的长度
3引用
1引用变量
inti=1;
int&r_i=i;
5名空间
1namespace
namespacecar
{
intmodel;
intlength;
intwidth;
}
namespaceplane
{
intmodel;
namespacesize
{
intlenth;
intwidth;
}
}
namespacecar//添加名空间的成员
{
char*name;
}
namespqcec=car;//定义别名
intTime//外不变量属于全局名空间
car:
:
length=3;
plane:
:
size:
:
length=70;
intTime=1996;
:
:
Time=1997;
16.2using
voidmain()
{
usingnamespacecar;
length;
usingnamespacephane;
model;
17.}
6new与delete运算符
double*pd; //definepointervariable
pd=newdouble;//allocatememory
if(pd!
=null)
{
...
deletepd;//freememory
}
18.double1=null
*pds=newdouble[100];
if(pds)
{
....
delete[]pds;
19.}
20.如果是使用new进行内存空间分配没有成功,则返回空指针null
释放一个数组分配的内存是,常常采用带[]的形式
7void指针它指向一个地址值,但是不说名数据类型,可以使用void指针创建一个通用的函数,在使用
的时候将指针类型转化成相应的具体类型。
voidShowHex(void*pv,intsiae)
{
....
((unsignedchar*)pv)[i]
}
voidmain()
{
void*pv=null;
unsignedcharc1=0x12;
pv=&c1;
ShowHex(pv,sizeof(c1));
}
9typeid运算符用来求得变量或队形爱女嘎的数据类型,返回一个type_info类型的对象,通过该对象
可以获取数据类型的名称
21.include
inti;
consttype_info&t0=typeid(i);
t0.name();
10函数
1模板函数
templateTmin(R&x,Tv&y)
{
returnxx:
y;
}
2指向函数的指针
intmax(intx,inty)
{
...
}
intmin(intx,inty)
{
...
}
int(*m_pfunction)(int,int);
voidmain()
{
m_pfunction=max;
(*m_pfunction)(2,3);
m_pfunction=min;
(*m_pfunction)(2,3);
}
11类与对象
1构在函数和析构函数
#include
#include
#include
classBook
{
private:
char*pBookName;
public:
intPageNum;
public:
Book(char*pBN=NULL);
~Book(void);
voidSetBookName(char*pBN);
intGetBookName(char*pBN,unsignedintMaxSize);
};
22.Book:
Book(char*PBN)
{
cout<<"构造函数"<pBookName=null;
if(oBN!
=null)
{
pBookName=newchar[strlen(pBN)+1];
if(pBookName!
=null)
strcyp(pBookName,pBN);
}
}
23.Book:
:
~Book()
{
delete[]pBookName;
}
24.voidBook:
:
SetBookName(char*pBN)
{
if(pBookName!
=null)
delete[]pBookName;
pBookName=newchar[strlen(pBN)+1];
if(pBookName!
=null)
strcyp(pBookName,pBN);
}
25.intBook:
:
GetBookName(char*pBN,unsignedintmaxSize)
{
if((pBookName!
=null))&&(MaxSize>strlen(pBookName))
{
strcpy(pBN,pBookName);
retrunstrlen(strlen(pBookName));
}
}
26.//使用
Bookb1;
b1.SetBookName("test");
Bookb2(test1);
2对象的引用参数传递
voidAdd(Bookb)
voidAddRef(Book&b);
3静态成员变量是一个公共变量
在初始化的时候利用作用运算符:
:
对私有类型的静态成员变量可以向公有类型的静态成变量一样赋值
但不能直接引用
3const类型成员函数与mutable
classCDate
{
public:
intyear;
mutableintmonth;
CDate(inty=2000,intm=1)
{
year=y;
month=m;
};
intBetMonth()const;//readonly
voidSetMonth(intm);//writeonly
}
voidCDate:
:
SetMonth(intm)
{
month=m;
}
voidmain()
{
CDated1;
}
27.在const类型的成员函数定义中,不可一直接或简介的修改普通类型的成员变量
如果象修改const类型对象的成员函数,可以使用关键字mutable对该成员变量进行修改
5对象的初始化与初始化行
将参数类表中的数值赋予成员变量时,不仅可以在构造函数的函数体中进行,以阿宽衣在初始化行中进行
在初始化处惊醒初始化的情况有:
1分层类的构在函数可以调用它的任何一个子对象的构造函数
2对常量const类型的成员变量的初始化必须在初始化行上
3对于引用类型的成员变量的初始化必须在初始化行上
classCPoint
{
public:
intx,y;
CPoint(intax=0,intay=0)
{
x=ax;
y=ay;
}
};
28.classCRect
{
private:
CPointlow_right;
CPointup_left;
public:
int&CenterX;
constintCenterY;
CRect(intx1,inty1,intx2,inty2,int&x3,inty3)
:
up_left(x1,y1),low_right(x2,y2),CenterX(x3),CenterY(y3)
{
}
};
voidmain()
{
intcx=5;
intcy=6;
CRectr1(1,2,3,4,cx,cy);
}
6拷贝构造函数
拷贝构造函数与普通构造函数的差别在与棋参数类表,参数列表中有一个对象,该对象的数据类型是
本类的一个引用,而且一般情况下,该对象还应该是一个const类型的对象。
如果在类的定义是不是显示的定义一个拷贝函数,则编译器会自动的定义一个拷贝构造函数
classCPoint
{
public:
intx,y;
CPoint(intm_x=0,ubtm_y=0); //defaultconstructor
cPoint(constCPoint&c); //copyconsrucotr
};
CPoint:
:
CPoint(intm_x,intm_y)
{
}
CPoint:
:
CPoint(constCPoint&c)
{
x=c.y;
y=c.x;
}
29.voidmain()
{
CPointc1(1,2);//invokedefaultconstructor
CPointc2-c1; //invokecopyconstructor
}
7templateclass
templateclassArray//templateclass
{
private:
Tarr[Size];
intCurSize;
public:
Array(T*date,intn)
{
CurSize=nn;Size;
for(inti=0;i {
Arr[i]=data[i];
}
}
}
30.voidmain()
{
inti1[10]={1,2,3,4,5,6,7,8,9};
Arrayarray_i1(i1,i0);
}
1友员类和友员函数
#include
#include
#include
31.classSoftWareEngineer;//先对SoftwareEngineer类进行显示说明一下
32.classComputer//定义Computer类
{
private:
intPrice;
public:
Computer(intp){Price=p};
friendclassSoftwareEngineer;//将友员类载这里声明
frinedvoidJudge(Computer&c,SoftwareEngineer&s)//友员函数
};
33.classSoftwareEngineer
{
intSalary;
charName[20];
public:
SoftwareEngineer(ints,char*n//构造函数)
{
Salary=s;
strcpy(Name,n);
}
intGetComputerPrice(Computer&c){returnc.Price}//访问Computer的思友变量
friendvoidJudge(Computer&c,SoftwareEngineer&s)//友员函数
};
//判断一个软件工程师是否可以用一个月的薪水买的器一台电脑
voidJudge(Computer&c,SoftwareEngineer&s)//桥友员函数
{
if(c.Price>s.Salary)
cout<<"软件工程师"< else
cout<<"软件工程师"<}
voidmain()
{
Computerc1(100);
SoftwareEngineers1(120,"user1");
Judge(c1,s1);
34.SiftwareEngineers2(80,"user2")
Judge(c1,s2);
getch();
}
2运算符重载
#include
#include
#include
35.classTValue{
private:
intvalue;
public:
TValue(inti){value=i}
//成员函数重载运算符
TValueoperator!
();
TValueoperator+(TValue&rv);
//友员函数重载运算符
friendostream&operator<<{ostream&os,TValue&rv};
}
36.TValueTvalue:
:
operator!
()
{
returnTValue(!
value);
}
37.TValueTValue:
operator+(TValue&rv)
{
returnTValue(value+rv.value);
}
38.ostream&operator<<(ostream&os,TValuerv)
{
os<returnos;
}
39.voidmain()
{
TValuev1(3),v2(5);
cout<<
}
3类的派生和继承
1classBox{
public:
intwidth,height;
voidSetWidth(intw){width=w};
voidSetWidth(inth){height=h;};
};
classTColorBox:
:
publicBox{
public:
intcolor;
voidSetColor(intc){color=c;};
};
40.voidmain(){
TColorBoxcb;
cb.SetColor(255);//声明非继承类的对象
cb.SetWidth(100);//声明继承类的对象
cb.SetHeight(100);//声明继承类的对象
}
2不能被继承的成员
构造函数,析构函数,用户定义的新操作符,用户定义的赋值操作,友员关系
3构造函数,析构函数的调用顺序
classA{
inta,b,c;
public:
A(intx,inty,intz)
{
a=x;
b=y;
c=z;
}
41.};
classB{
intd;
public:
B