对象指针是指指向对象的指针,this指针是隐含于每一个类的成员函数中的特殊指针(包括构造、析构函数),用于指向正在被操作的对象。
指向非静态数据成员的指针需要被声明为类型(类名:
:
*指针名)
指向静态数据成员的指针需要被声明为类型*指针名
同理,非静态的数据成员也需要建立在类对象的基础上进行访问
5、静态数据成员和友元
静态数据成员具有静态生存期,可以用类名:
:
标识符进行访问,由该类的所有对象共同维护和使用
必须在文件作用域对静态数据成员进行初始化,如:
intCmcPoint:
:
c_n_count=0;
//必须进行初始化定义性说明
友元函数是在类中定义的非成员函数(所以也不能引用this指针),如:
frienddoubleget_length(constCmcPoint&p1,constCmcPoint&p2);
//返回两个点之间的距离(友元函数)
//不能用const修饰!
?
?
?
?
尽量传引用?
?
?
友元函数说明如下:
1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数**的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用":
:
"指示属于哪个类,只有成员函数才使用":
:
"作用域符号;
3)友元函数不能直接访问类的成员,只能访问对象成员,
4)友元函数可以访问对象的私有成员,但普通函数不行;
5)调用友元函数时,在实际参数中需要指出要访问的对象,
6)类与类之间的友元关系不能继承。
类的友元关系说明如下:
(1)友元关系不可继承
(2)友元关系是单向的
(3)友元关系是不可传递的
6、函数的重载和默认形参
定义:
两个以上的函数,具有相同函数名和类似的功能,但是形参的个数和类型不同,编译器在调用时对其进行自动匹配。
(C++语法,C语言不支持重载)
能作为重载的区分:
1、函数的形参个数
2、函数的形参类型
3、常函数的const标识符
待默认形参的函数:
声明时时只能从右向左缺省,而且必须给出缺省值
定义时(实现时)不必给出缺省值
7、运算符重载
运算符重载是指对已有的运算符赋予多重含义,使得同一个运算符作用与不同类型的数据时,导致不同的行为。
可以以友元函数的形式或者类成员函数的形式。
不能重载的运算符:
类属运算符(.)成员指针运算符(.*)作用域运算符(:
:
)sizeof运算符和
三目运算符(?
:
)
关于运算符(++,--)前置后置问题,如
Clock&operator++();//前置单目运算符重载
Clock&operator++(int);//后置单目运算符重载
调用时像正常形式调用就行
关于CString的运算符重载(声明部分):
classCMyString
{
public:
CMyString(inti_n_length=INIT_SIZE);//默认构造函数
CMyString(constchar*i_c_str);//含有一个参数的构造函数
CMyString(constCMyString&i_s_str);//拷贝构造函数
~CMyString();//析构函数
CMyStringoperator=(constCMyStringi_s_str);//重载赋值运算符
CMyStringoperator+(constCMyStringi_s_str);//重载加法运算符
CMyStringoperator+=(constCMyStringi_s_str);//重载加等于运算符
friendbooloperator==(constCMyString&i_s_str1,constCMyString&i_s_str2);
//重载判断是否相等运算符
voidshow()const;//打印字符串
private:
char*m_c_path;//初始地址
intm_n_length;//当前字符串长度
intm_n_max_size;//最大的储存空间
};
实现部分(基于):
CMyString:
:
CMyString(inti_n_length/*INIT_SIZE*/)//默认构造函数
{
this->m_c_path=(char*)malloc(sizeof(char)*i_n_length);
this->m_c_path[0]='\0';
m_n_length=0;
m_n_max_size=i_n_length;
}
CMyString:
:
CMyString(constchar*i_c_str)//含有一个参数的构造函数
{
this->m_n_length=strlen(i_c_str);
this->m_n_max_size=strlen(i_c_str);
this->m_c_path=(char*)malloc(sizeof(char)*this->m_n_length);
strcpy(this->m_c_path,i_c_str);
}
CMyString:
:
CMyString(constCMyString&i_s_str)//拷贝构造函数
{
this->m_c_path=(char*)malloc(sizeof(char)*i_s_str.m_n_max_size);
for(inti=0;i<=i_s_str.m_n_length/*!
!
!
不是-1!
!
!
*/;i++)
this->m_c_path[i]=i_s_str.m_c_path[i];
this->m_n_length=i_s_str.m_n_length;
this->m_n_max_size=i_s_str.m_n_max_size;
}
CMyString:
:
~CMyString()
{
this->m_c_path=NULL;
this->m_n_length=0;
this->m_n_max_size=0;
}
CMyStringCMyString:
:
operator=(constCMyStringi_s_str)
{
free(this->m_c_path);
this->m_c_path=(char*)malloc(sizeof(char)*i_s_str.m_n_max_size);
this->m_n_length=i_s_str.m_n_length;
this->m_n_max_size=i_s_str.m_n_max_size;
for(inti=0;i<=i_s_str.m_n_length/*!
!
!
不是-1!
!
!
*/;i++)
this->m_c_path[i]=i_s_str.m_c_path[i];
returni_s_str;
}
CMyStringCMyString:
:
operator+(constCMyStringi_s_str)
{
CMyStringc_str(this->m_n_max_size+i_s_str.m_n_max_size);
c_str.m_n_max_size=this->m_n_max_size+i_s_str.m_n_max_size;
c_str.m_n_length=this->m_n_length+i_s_str.m_n_length;
c_str.m_c_path=(char*)malloc(sizeof(char)*c_str.m_n_max_size);
strcpy(c_str.m_c_path,this->m_c_path);
strcat(c_str.m_c_path,i_s_str.m_c_path);
c_str.m_c_path[c_str.m_n_length]='\0';
returnc_str;
}
CMyStringCMyString:
:
operator+=(constCMyStringi_s_str)
{
this->m_n_max_size+=i_s_str.m_n_max_size;
this->m_c_path=(char*)realloc(this->m_c_path,
sizeof(char)*this->m_n_max_size);
this->m_c_path[this->m_n_length]='\0';
this->m_n_length+=i_s_str.m_n_length;
strcat(this->m_c_path,i_s_str.m_c_path);//需要足够的空间
return(*this);
}
voidCMyString:
:
show()const//打印字符串
{
cout<m_c_path<}
booloperator==(constCMyString&i_s_str1,constCMyString&i_s_str2)
//重载判断是否相等运算符
{
if(i_s_str1.m_c_path==NULL||i_s_str2.m_c_path==NULL)
{
if(i_s_str1.m_c_path!
=i_s_str2.m_c_path)//不同时为空
returnfalse;
}
if(i_s_str1.m_n_length!
=i_s_str2.m_n_length)
returnfalse;
if(strcmp(i_s_str1.m_c_path,i_s_str2.m_c_path)!
=0)
returnfalse;
returntrue;
}
微软的实现方式:
char*strcpy(char*dst,constchar*src)
{
char*cp=dst;
while(*cp++=*src++)
;//Copysrcoverdst
return(dst);
}//Microsoft的实现方式
intstrcmp(constchar*src,constchar*dst)
{
intret=0;
while(!
(ret=*(unsignedchar*)src-*(unsignedchar*)dst)&&*dst)
++src,++dst;
if(ret<0)
ret=-1;
elseif(ret>0)
ret=1;
return(ret);
}//Microsoft的实现方式
char*strcat(char*dst,constchar*src)
{
char*cp=dst;
while(*cp)
cp++;//findendofdst
while(*cp++=*src++);//Copysrctoendofdst
return(dst);//returndst
}//Microsoft的实现方式
8、类的继承和派生
构造函数和析构函数不会被继承
私有继承和protected类型继承会把父类的public成员变成对应的类型,有多个基类(多继承),一个(单继承)
从两个不同基类继承来的同名成员,如果不在本类进行覆盖,直接调用,将会导致二义性,编译无法通过,需要利用作用域运算符,会有两份内存分配。
默认为私有继承。
公有继承:
public和protected类属性不变,private类被隐藏,不能直接进行调用。
保护继承:
public和protected类以protected类的属性出现,私有成员不能直接访问。
私有继承:
public和protected类以private类的属性出现,私有成员不能直接访问。
构造派生类对象时,就要对基类数据成员、新增数据成员和成员对象的数据成员进行初始化。
9、虚基类、虚函数、纯虚函数
将继承中的共同基类设置成虚基类,这时从不同路径继承来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射,不会出现二义性。
如:
classCmcBird:
virtualpublicCmcAnimal
//将CmcAnimal视为虚基类
classCmcHorse:
virtualpublicCmcAnimal
//将CmcAnimal视为虚基类
classCmcFlyHorse:
publicCmcBird,publicCmcHorse
如果使用非默认形式的构造函数,在整个继承关系中,直接或者间接继承虚基类的所有派生类,都必须在构造函数初始化列表中给出初始化。
虚函数是动态绑定的基础,且必须是非静态的成员函数。
(如果认为该类会被继承,最好声明其方法为虚函数,但是需要生成虚函数列表,会一定程度上影响性能)
通俗来看,如果一个基类成员函数不是虚函数的话,其派生类将该成员函数覆盖后,利用该基类的指针(或引用),即使指向(引用)的是一个派生类的实例,也只能对基类的函数进行调用。
如:
CmcAnimal*animal=newCmcAnimal;
CmcAnimal*bird=newCmcBird;
CmcAnimal*horse=newCmcHorse;
animal->move();
horse->move();
bird->move();
其中CmcBird、CmcHorse类分别继承了CmcAnimal类,并覆盖了其move()方法,如果在CmcAnimal类中,move()不是虚函数,运行结果为:
如果用virtual标识符对CmcAnimal的move函数进行声明(定义时不必加),即:
virtualvoidmove();//动物的移动函数
运行结果为:
即基类指针分别正确调用了子类的move()方法
注意:
不能声明类的虚构造函数,析构函数可以,用来指向性清理
抽象类是为了抽象和设计为目的建立的,不能实例化其本身,只能实例其非抽象派生类。
抽象类带有纯虚函数。
纯虚函数是特殊的虚函数(比虚函数还虚……),声明举例如下:
virtualvoidmove()=0;//动物的移动函数,纯虚函数,必须进行覆盖
声明了纯虚函数后,基类中就不必给出其实现部分【试了一下,给出实现部分也能编译通过,但是没有任何实际意义,因为子类肯定会覆盖它】,函数体由派生类给出。
因为你无法使用基类指针指向基类,如:
//CmcAnimal*animal=newCmcAnimal;
//error因为包含纯虚函数,因此CmcAnimal是一个抽象类
所以无法通过指针调用纯虚函数在基类中的实现,因此完全没有意义。
但是你可以声明抽象基类的指针指向非抽象子类,这是正确的,而且能调用正确的方法:
CmcAnimal*bird=newCmcBird;
CmcAnimal*horse=newCmcHorse;
但是但是,如果通过这种方法:
CmcHorsehorse2;
horse2.CmcAnimal:
:
move();
//ok!
可以利用作用域进行直接调用,虽然是纯虚函数,但是可以被调用!
//不过既然作为纯虚函数,有定义应该是件不适合的事情
强行通过子类实例的作用域运算符对其调用,可以使用该纯虚函数!
!
!
——非常有趣
这样做未免有点过于较劲了,而忽略了纯虚函数的抽象类的目的,只有你绝对肯定不需要使用该类的对象和其成员方法时,你才会将其声明为抽象类和纯虚函数。
最后,如果一个类由抽象类派生,但没有