C++和C的区别与发展总结
2016-08-05 09:13
495 查看
之前C++学得就不太扎实,正好看到《VC++深入详解》第二章对C++进行了一个简单总结,故整理回顾之。
C与C相比的特性
从结构到类
构造函数
函数的重载
析构函数
this指针
类的继承
继承
在子类中调用父类的带参数的构造函数
多重继承
虚函数与多态性纯虚函数
虚函数与多态性
纯虚函数
函数的覆盖和隐藏
覆盖
隐藏
引用
C类的设计习惯及头文件重复包含问题的解决
封装性把数据与操作数据的函数组织在一起,不仅使程序结构更加紧凑,并且提高了类内部数据的安全性。
继承性
继承性增加了软件的可扩充性及代码重用性。
多态性
多态性使设计人员在设计程序时可以对问题进行更好的抽象,有利于代码的维护和可重用。
类的关键字是class ; 结构的关键字是struct。
成员的访问控制方面:
结构体默认情况下,其成员是公有(public)的;
类默认情况下,其成员是私有(private)的;
在一个类中,公有成员是可以在类的外部进行访问的,而私有成员就只能在类的内部进行访问。
例如下面的程序就会报错,因为x和y都是默认的私有成员,只有在类的内部才能进行访问
为了解决上面的问题,使得可以对类内的成员进行初始化,我们用构造函数进行。先来看没有构造函数直接进行输出是x,y的值:
在没有进行初始化的条件下,输出为x=-858993460,y=-858993460(或者其它毫不相关的数)。
下面引入构造函数后:
在类中定义成员变量时,不能直接给成员变量赋初值,而是通过构造函数进行。
C++规定构造函数的名字和类名相同,没有返回值。
构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
构造函数不需要手动调用,当在main函数中执行“point pt”这条语句时,就会自动调用point这个类的构造函数,从而完成对pt对象内部数据成员x和y的初始化工作。
如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:
如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
在类中所有非静态的对象数据成员,他们所属的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
可以看到在上述代码中,有两个构造函数,他们的函数名一样,只是参数的类型和个数不一样,这就是C++中函数的重载。
当执行point pt(6, 8)这条语句时,C++编译器将根据参数的类型和参数的个数来确定执行哪一个构造函数,在上例中,执行point(int a, int b)函数。
函数的重载不仅仅局限于构造函数,所有C++中满足函数名相同,但是函数的参数类型、参数个数不同都可以构成函数的重载。
只有函数的返回类型不同是不能构成函数的重载的。
void output()
int output()
上述两个不能构成函数的重载。
析构函数的定义格式为:~类名() 如~point()
析构函数不允许有返回值。
析构函数不允许带参数。
一个类中只能有一个析构函数。
对一个对象来说,析构函数是最后一个被调用的成员函数。
例如下面这段代码:
例一
输出为:
那么为什么输出不是10, 10呢,因为在input函数中,point类的成员变量x和y都是不可见的。并不是说成员变量在成员函数中不可见,而是如果成员函数中定义了和成员变量相同的变量,则成员变量在该成员函数中不可见,在下一个例子中可以说明这一点。
例二
输出为:
在这个例子中,从输出x=5,y=10可以看出:成员变量y在成员函数input中就是可见的, 成员变量x在成员函数input中是不可见的。
那么该如何利用成员函数给成员变量赋值呢,可以用下面两个方法:
例三
输出为:
例四
利用this指针,this指针是一个隐藏的指针,它指向对象本身,代表了对象的地址。
例如上面几个例子中的对象pt,this=&pt。
所有对数据成员的访问都隐含地被加上了前缀this->,例如,x=0,等价于this->x=0。
所以我们可以这样写这个程序:
输出为:
其中fish继承自animal类。
animal类称为基类,也称为父类。
fish类称为派生类,也称为子类。
声明方法为 “class 派生类名称:访问权限修饰符 基类名称”。
访问权限修饰符为public:
基类中的成员在派生类中仍以原来的访问权限在派生类中出现。
如果没有指定,则默认是private:
基类中的成员在派生类中都变成了private类型的访问权限。
访问权限修饰符为protected:
基类中的public和protected成员在派生类中都变成了protected类型的访问权限。
基类中的private成员不能被派生类访问。
补充:类中成员访问权限修饰符:
public:成员可以在任何地方被访问,其它地方访问要加上”对象名.成员“。
protected: 成员只能在该类及其子类中访问。
private: 成员只能在该类自身中访问,派生类中也不能访问。
派生类除了自己的成员变量和成员方法外,还可以继承基类的成员变量和成员方法。
接下来看一下子类和父类的构造函数和析构函数的顺序:
输出为:
可以看出,在声明子类对象时,父类的构造函数先运行,然后子类的构造函数,在对象声明周期结束时,子类的析构函数先运行,然后是父类的析构函数。
在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会调用父类的带参数的构造函数去构造对象。
定义形式为:
class 派生类名 : 访问权限 基类名称, 访问权限 基类名称
{
……
};
例如B类是由类C和类D派生的,可按如下方式进行说明:
class B : public C, public D
{
……
};
下面通过两个例子来对比说明:
例一
输出为:
例二
输出为:
为什么例一中可以直接将fish类的对象fh的地址赋值给animal类的指针变量pAn?
因为fish对象也是一个animal对象,对fish类型转换为animal类型不用强制类型转换,C++编译器会自动进行这种转换。反过来,则不能把animal对象看成fish对象。
为什么例一的输出结果为“animal breathe”而不是“fish bubble”呢?
因为我们将fish类的对象fh的地址赋值给pAn时,C++编译器进行了类型转换,此时C++编译器认为变量pAn保存的就是animal对象的地址。
fish类对象所占的内存图如下所示,它分为两部分。
animal的对象所占内存;
fish的对象自身增加的部分。
当我们将fish类的对象转换为animal类型时,该对象就被认为是原对象整个内存模型的上半部分,也就是“animal的对象所占内存”。当我们利用类型转换后的对象指针去调用它的方法时,自然也就是调用它所在的内存中的方法。
用virtual关键字申明的函数叫做虚函数。
对于例二输出为“fish bubble”,这就是C++的多态性。当C++编译器在编译的时候,发现animal类的breathe()是虚函数,这时C++就会采用迟绑定技术。也就是编译时不确定具体调用的函数,而是在运行时,依据对象的类型(在程序中,我们传递的是fish类对象的地址)来确认调用的是哪一个函数,这种能力叫做C++的多态性。我们没有在breathe()函数前加virtual关键字时,C++编译器在编译时就确定了哪个函数被调用,这叫做早期绑定。
纯虚函数是指被标明为不具体实现的虚成员函数。
纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。
凡是含有纯虚函数的类叫做抽象类,这种类不能声明对象,只能作为基类为派生类服务。
在派生类中,必须完全实现基类的纯虚函数,否则派生类也成了抽象类,不能实例话对象。
基类函数必须是虚函数;
发生覆盖的两个函数分别位于基类和派生类中;
函数名称与参数列表必须完全相同。
例:
上述例子中,fish类中的breathe()函数就实现了对animal类中breathe函数的覆盖。fish类中的breathe()函数仍然是虚函数。
与覆盖中的代码相比,此段代码中,派生类fish和基类animal中的breathe函数也是完全一样的。不同的是breathe函数不是虚函数,这种情况称为函数的隐藏。所谓隐藏,是指派生类中具有与基类同名的函数(不考虑参数列表是否相同),从而在派生类中隐藏了基类的同名函数。
两种函数隐藏的情况:
派生类的函数与基类的函数完全相同(函数名和参数列表都相同),只是基类的函数没有使用virtual关键字。此时基类的函数将被隐藏,而不是覆盖。
派生类的函数与基类的函数同名,但参数列表不同,在这种情况下,不管基类的函数声明是否有virtual关键字,基类的函数都将被隐藏(覆盖的条件是函数名和参数列表都相同,且基类中函数用virtual关键字修饰)。
例二
在Derived类中的fn()函数隐藏了Base类中的fn()函数,因此Derived类中的fn()函数不是虚函数。因为两个函数的参数列表不同。
在Derived2类中的fn()函数覆盖类Derived类中的fn()函数。因为Derived2类中的fn()函数和Base类中的fn()虚函数具有相同的函数名和参数列表,因此Derived2类中的fn()函数是虚函数。注意:在Derived2中,Base类的fn()函数是不可见的,但这并不影响fn函数的覆盖,因为Derived2也是Base类的派生类。
当隐藏发生时,如果在派生类的同名函数中想要调用基类的被隐藏函数,可以使用类名::函数名(参数)的语法形式。
例一
用&表示申明一个引用,引用必须在申明时进行初始化。
上例中,并不是将b变成c的引用,而是给b赋值,此时b和a的值都变成了3。
例二
上述例子中,不能将函数定义成void change(int a, int b)。 如果定义成这样,在调用完成之后,x还是等于原来的x,y还是等于原来的y,因为函数中的a=x,b=y,而x!=a,y!=b。使用引用就不同了,使用引用后,a和x,b和y事实上是相同的,因为他们在内存中占用的是同一个内存单元。
对于头文件重复包含的情况,如main.cpp包含了animal.h文件和fish.h文件,而fish.h文件又包含了animal.h文件。这样就会出现头文件重复包含了,编译报错:‘class’ type redefinition。解决方法如下,在每一个头文件中,都使用条件预处理指令,如下:
C与C相比的特性
从结构到类
构造函数
函数的重载
析构函数
this指针
类的继承
继承
在子类中调用父类的带参数的构造函数
多重继承
虚函数与多态性纯虚函数
虚函数与多态性
纯虚函数
函数的覆盖和隐藏
覆盖
隐藏
引用
C类的设计习惯及头文件重复包含问题的解决
1.C++与C相比的特性
封装性封装性把数据与操作数据的函数组织在一起,不仅使程序结构更加紧凑,并且提高了类内部数据的安全性。
继承性
继承性增加了软件的可扩充性及代码重用性。
多态性
多态性使设计人员在设计程序时可以对问题进行更好的抽象,有利于代码的维护和可重用。
2.从结构到类
C++中不管是结构还是类,都可以包含函数。区别是:类的关键字是class ; 结构的关键字是struct。
成员的访问控制方面:
结构体默认情况下,其成员是公有(public)的;
类默认情况下,其成员是私有(private)的;
在一个类中,公有成员是可以在类的外部进行访问的,而私有成员就只能在类的内部进行访问。
例如下面的程序就会报错,因为x和y都是默认的私有成员,只有在类的内部才能进行访问
#include <iostream.h> using namespace std; class point { int x; int y; void output() { cout << x << endl << y << endl; } }; int main(void) { point pt; pt.x = 0; //无法访问 pt.y = 0; //无法访问 pt.output(); //无法访问 return 0; }
为了解决上面的问题,使得可以对类内的成员进行初始化,我们用构造函数进行。先来看没有构造函数直接进行输出是x,y的值:
#include <iostream.h> using namespace std; class point { public: int x; int y; void output() { cout << x << endl << y << endl; } }; int main(void) { point pt; pt.output(); return 0; }
在没有进行初始化的条件下,输出为x=-858993460,y=-858993460(或者其它毫不相关的数)。
下面引入构造函数后:
#include <iostream.h> using namespace std; class point { public: int x; int y; point() //point的构造函数 { x = 0; y = 0; } void output() { cout << x << endl << y << endl; } }; int main(void) { point pt; pt.output(); return 0; }
3.构造函数
构造函数,用来对类中的成员变量进行初始化。在类中定义成员变量时,不能直接给成员变量赋初值,而是通过构造函数进行。
class point { int x = 0;//错误,此处不能给变量x赋值 int y; };
C++规定构造函数的名字和类名相同,没有返回值。
构造函数的作用是对对象本身做初始化工作,也就是给用户提供初始化类中成员变量的一种方式。
构造函数不需要手动调用,当在main函数中执行“point pt”这条语句时,就会自动调用point这个类的构造函数,从而完成对pt对象内部数据成员x和y的初始化工作。
如果一个类中没有定义任何的构造函数,那么编译器只有在以下三种情况,才会提供默认的构造函数:
如果类有虚拟成员函数或者虚拟继承父类(即有虚拟基类)时;
如果类的基类有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数);
在类中所有非静态的对象数据成员,他们所属的类中有构造函数(可以是用户定义的构造函数,或编译器提供的默认构造函数)。
4.函数的重载
下面先看一个代码:#include <iostream.h> using namespace std; class point { public: int x; int y; point() //point的第一个构造函数 { x = 0; y = 0; } point(int a, int b) //point的第二个构造函数 { x = a; y = b; } void output() { cout << x << endl << y << endl; } }; int main(void) { point pt(6, 8); pt.output(); return 0; }
可以看到在上述代码中,有两个构造函数,他们的函数名一样,只是参数的类型和个数不一样,这就是C++中函数的重载。
当执行point pt(6, 8)这条语句时,C++编译器将根据参数的类型和参数的个数来确定执行哪一个构造函数,在上例中,执行point(int a, int b)函数。
函数的重载不仅仅局限于构造函数,所有C++中满足函数名相同,但是函数的参数类型、参数个数不同都可以构成函数的重载。
只有函数的返回类型不同是不能构成函数的重载的。
void output()
int output()
上述两个不能构成函数的重载。
5.析构函数
当一个对象的声明周期结束时,我们应该去释放这个对象所占有的资源,这可以利用析构函数来完成。析构函数的定义格式为:~类名() 如~point()
析构函数不允许有返回值。
析构函数不允许带参数。
一个类中只能有一个析构函数。
对一个对象来说,析构函数是最后一个被调用的成员函数。
例如下面这段代码:
class Student { private: char *pName; public: Student() { pName = new char[20]; } ~Student() { delete[] pName; //如果类中没有用到指针,则析构函数内部空着就好 } };
6.this指针
先来看几个例子:例一
#include <iostream.h> using namespace std; class point { public: int x; int y; point() { x = 0; y = 0; } point(int a, int b) { x = a; y = b; } void output() { cout << x << endl << y << endl; } void input(int x, int y) { x = x; y = y; } }; int main(void) { point pt(5, 5); pt.input(10, 10); pt.output(); return 0; }
输出为:
5 5
那么为什么输出不是10, 10呢,因为在input函数中,point类的成员变量x和y都是不可见的。并不是说成员变量在成员函数中不可见,而是如果成员函数中定义了和成员变量相同的变量,则成员变量在该成员函数中不可见,在下一个例子中可以说明这一点。
例二
#include <iostream.h> using namespace std; class point { public: int x; int y; point() { x = 0; y = 0; } point(int a, int b) { x = a; y = b; } void output() { cout << x << endl << y << endl; } void input(int x) { y = x; } }; int main(void) { point pt(5, 5); pt.input(10); pt.output(); return 0; }
输出为:
5 10
在这个例子中,从输出x=5,y=10可以看出:成员变量y在成员函数input中就是可见的, 成员变量x在成员函数input中是不可见的。
那么该如何利用成员函数给成员变量赋值呢,可以用下面两个方法:
例三
#include <iostream.h> using namespace std; class point { public: int x; int y; point() { x = 0; y = 0; } point(int a, int b) { x = a; y = b; } void output() { cout << x << endl << y << endl; } void input(int c, int d) { x = c; y = d; } }; int main(void) { point pt(5, 5); pt.input(10, 10); pt.output(); return 0; }
输出为:
10 10
例四
利用this指针,this指针是一个隐藏的指针,它指向对象本身,代表了对象的地址。
例如上面几个例子中的对象pt,this=&pt。
所有对数据成员的访问都隐含地被加上了前缀this->,例如,x=0,等价于this->x=0。
所以我们可以这样写这个程序:
#include <iostream.h> using namespace std; class point { public: int x; int y; point() { x = 0; y = 0; } point(int a, int b) { x = a; y = b; } void output() { cout << x << endl << y << endl; } void input(int x, int y) { this->x = x; this->y = x; } }; int main(void) { point pt(5, 5); pt.input(10, 10); return 0; }
输出为:
10 10
7.类的继承
继承
先看下面这个例子:#include <iostream.h> using namespace std; class animal { public: void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } void breathe() { cout << "animal breathe" << endl; } }; class fish:public animal { }; int main(void) { animal an; fish fh; an.eat(); fh.eat(); return 0; }
其中fish继承自animal类。
animal类称为基类,也称为父类。
fish类称为派生类,也称为子类。
声明方法为 “class 派生类名称:访问权限修饰符 基类名称”。
访问权限修饰符为public:
基类中的成员在派生类中仍以原来的访问权限在派生类中出现。
如果没有指定,则默认是private:
基类中的成员在派生类中都变成了private类型的访问权限。
访问权限修饰符为protected:
基类中的public和protected成员在派生类中都变成了protected类型的访问权限。
基类中的private成员不能被派生类访问。
补充:类中成员访问权限修饰符:
public:成员可以在任何地方被访问,其它地方访问要加上”对象名.成员“。
protected: 成员只能在该类及其子类中访问。
private: 成员只能在该类自身中访问,派生类中也不能访问。
派生类除了自己的成员变量和成员方法外,还可以继承基类的成员变量和成员方法。
接下来看一下子类和父类的构造函数和析构函数的顺序:
#include <iostream.h> using namespace std; class animal { public: animal() { cout << "animal construct" << endl; } ~animal() { cout << "animal destruct" << endl; } void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } void breathe() { cout << "animal breathe" << endl; } }; class fish:public animal { public: fish() { cout << "fish construct" << endl; } ~fish() { cout << "fish destruct" << endl; } }; int main(void) { fish fh; return 0; }
输出为:
animal construct fish construct fish destruct animal destruct
可以看出,在声明子类对象时,父类的构造函数先运行,然后子类的构造函数,在对象声明周期结束时,子类的析构函数先运行,然后是父类的析构函数。
在子类中调用父类的带参数的构造函数
#include <iostream.h> using namespace std; class animal { public: animal(int height, int weight) { cout << "animal construct" << endl; } }; class fish:pubilc animal { pubilc: fish():animal(400, 300) { cout << "fish construct" << endl; } }; int main(void) { fish fh; return 0; }
在fish类的构造函数后,加一个冒号(:),然后加上父类的带参数的构造函数。这样,在子类的构造函数被调用时,系统就会调用父类的带参数的构造函数去构造对象。
多重继承
如同该名字中所描述的,一个类可以从多个基类中派生。定义形式为:
class 派生类名 : 访问权限 基类名称, 访问权限 基类名称
{
……
};
例如B类是由类C和类D派生的,可按如下方式进行说明:
class B : public C, public D
{
……
};
8.虚函数与多态性、纯虚函数
虚函数与多态性
C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象是派生类,就调用派生类的函数;如果对象是基类,就调用基类的函数。C++的多态性是由虚函数类实现的,而不是纯虚函数。下面通过两个例子来对比说明:
例一
#include <iostream.h> using namespace std; class animal { public: void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } void breathe() { cout << "animal breathe" << endl; } }; class fish:public animal { public: void breathe() { cout << "fish bubble" << endl; } }; void fn(animal *pAn) { pAn->breathe(); } int main(void) { animal *pAn; fish fh; pAn = &fh; fn(pAn); return 0; }
输出为:
animal breathe
例二
#include <iostream.h> using namespace std; class animal { public: void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } virtual void breathe() { cout << "animal breathe" << endl; } }; class fish:public animal { public: void breathe() { cout << "fish bubble" << endl; } }; void fn(animal *pAn) { pAn->breathe(); } int main(void) { animal *pAn; fish fh; pAn = &fh; fn(pAn); return 0; }
输出为:
fish bubble
为什么例一中可以直接将fish类的对象fh的地址赋值给animal类的指针变量pAn?
因为fish对象也是一个animal对象,对fish类型转换为animal类型不用强制类型转换,C++编译器会自动进行这种转换。反过来,则不能把animal对象看成fish对象。
为什么例一的输出结果为“animal breathe”而不是“fish bubble”呢?
因为我们将fish类的对象fh的地址赋值给pAn时,C++编译器进行了类型转换,此时C++编译器认为变量pAn保存的就是animal对象的地址。
fish类对象所占的内存图如下所示,它分为两部分。
animal的对象所占内存;
fish的对象自身增加的部分。
当我们将fish类的对象转换为animal类型时,该对象就被认为是原对象整个内存模型的上半部分,也就是“animal的对象所占内存”。当我们利用类型转换后的对象指针去调用它的方法时,自然也就是调用它所在的内存中的方法。
用virtual关键字申明的函数叫做虚函数。
对于例二输出为“fish bubble”,这就是C++的多态性。当C++编译器在编译的时候,发现animal类的breathe()是虚函数,这时C++就会采用迟绑定技术。也就是编译时不确定具体调用的函数,而是在运行时,依据对象的类型(在程序中,我们传递的是fish类对象的地址)来确认调用的是哪一个函数,这种能力叫做C++的多态性。我们没有在breathe()函数前加virtual关键字时,C++编译器在编译时就确定了哪个函数被调用,这叫做早期绑定。
纯虚函数
class animal { public: void eat() { cout << "animal eat" << endl; } void sleep() { cout << "animal sleep" << endl; } virtual void breathe() = 0; };
纯虚函数是指被标明为不具体实现的虚成员函数。
纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。
凡是含有纯虚函数的类叫做抽象类,这种类不能声明对象,只能作为基类为派生类服务。
在派生类中,必须完全实现基类的纯虚函数,否则派生类也成了抽象类,不能实例话对象。
9.函数的覆盖和隐藏
覆盖
构成函数覆盖的条件:基类函数必须是虚函数;
发生覆盖的两个函数分别位于基类和派生类中;
函数名称与参数列表必须完全相同。
例:
class animal { public: ... virtual void breathe() { cout << "animal breathe" << endl; } ... }; class fish:public animal { public: ... void breathe() { cout << "fish bubble" << endl; } ... };
上述例子中,fish类中的breathe()函数就实现了对animal类中breathe函数的覆盖。fish类中的breathe()函数仍然是虚函数。
隐藏
例一class animal { public: ... void breathe() { cout << "animal breathe" << endl; } ... }; class fish:public animal { public: ... void breathe() { cout << "fish bubble" << endl; } ... };
与覆盖中的代码相比,此段代码中,派生类fish和基类animal中的breathe函数也是完全一样的。不同的是breathe函数不是虚函数,这种情况称为函数的隐藏。所谓隐藏,是指派生类中具有与基类同名的函数(不考虑参数列表是否相同),从而在派生类中隐藏了基类的同名函数。
两种函数隐藏的情况:
派生类的函数与基类的函数完全相同(函数名和参数列表都相同),只是基类的函数没有使用virtual关键字。此时基类的函数将被隐藏,而不是覆盖。
派生类的函数与基类的函数同名,但参数列表不同,在这种情况下,不管基类的函数声明是否有virtual关键字,基类的函数都将被隐藏(覆盖的条件是函数名和参数列表都相同,且基类中函数用virtual关键字修饰)。
例二
class Base { public: virtual void fn(); }; class Derived:public Base { public: void fn(int); }; class Derived2:public Derived { public: void fn(); };
在Derived类中的fn()函数隐藏了Base类中的fn()函数,因此Derived类中的fn()函数不是虚函数。因为两个函数的参数列表不同。
在Derived2类中的fn()函数覆盖类Derived类中的fn()函数。因为Derived2类中的fn()函数和Base类中的fn()虚函数具有相同的函数名和参数列表,因此Derived2类中的fn()函数是虚函数。注意:在Derived2中,Base类的fn()函数是不可见的,但这并不影响fn函数的覆盖,因为Derived2也是Base类的派生类。
当隐藏发生时,如果在派生类的同名函数中想要调用基类的被隐藏函数,可以使用类名::函数名(参数)的语法形式。
10.引用
引用就是一个变量的别名。它需要用另一个变量或对象来初始化自身。引用就像一个人的外号一样。例一
//下面的代码生命了一个引用b,并用变量a进行了初始化 int a = 5; int &b = a;
用&表示申明一个引用,引用必须在申明时进行初始化。
int a = 5; int& b = a ; int c = 3; b = c;
上例中,并不是将b变成c的引用,而是给b赋值,此时b和a的值都变成了3。
例二
#include <iostream.h> using namespace std; //change函数主要用来交换a和b的值 void change(int& a, int& b); int main(void) { int x = 5; int y = 3; cout << "original x = " << x << endl; cout << "original y = " << y << endl; change(x, y); //此处如果用指针传递,则调用change(&x, &y),这样很容易让人迷惑,不知道交换的是x和y的值,还是x和y的地址?此处使用引用,可读性就比指针要好 cout << "changed x = " << x << endl; cout << "changed y = " << y << endl; return 0; } /* change()函数中采用了一个巧妙的算法来实现了a和b值的互换 */ void change(int& a, int& b) { a = a+b; b = a-b; a = a-b; }
上述例子中,不能将函数定义成void change(int a, int b)。 如果定义成这样,在调用完成之后,x还是等于原来的x,y还是等于原来的y,因为函数中的a=x,b=y,而x!=a,y!=b。使用引用就不同了,使用引用后,a和x,b和y事实上是相同的,因为他们在内存中占用的是同一个内存单元。
11.C++类的设计习惯及头文件重复包含问题的解决
在设计一个类的时候,通常是将类的定义及类成员函数的声明放到头文件(即.h文件)中,将类中成员函数的实现放到源文件(即.cpp)中。对于main()函数,我们则单独把它放到main.cpp文件中。对于头文件重复包含的情况,如main.cpp包含了animal.h文件和fish.h文件,而fish.h文件又包含了animal.h文件。这样就会出现头文件重复包含了,编译报错:‘class’ type redefinition。解决方法如下,在每一个头文件中,都使用条件预处理指令,如下:
#ifndef _ANIMAL_H_ #define _ANIMAL_H_ class animal { public: animal(); ~animal(); void eat(); void sleep(); virtual void breathe(); }; #endif
相关文章推荐
- 总结C++与C#的区别
- C++ 字符类型总结&CString ,BSTR ,LPCTSTR之间关系和区别
- 简单来总结一下C++与C#之间的区别
- c#学习之与c++的语法区别总结
- c++构造函数总结----C++拷贝构造函数及重写operator =的区别
- C和C++的区别总结
- c++ 字符类型总结区别wchar_t,char,WCHAR
- 总结系列(opencv中c版本和c++版本区别体验,续...)
- c++ 中 sort 与 c 的去sort cmp 比较函数 书写区别,待总结
- c++中关于类(class)和结构体(struct)的区别个人总结
- 面试总结(二)转 C++的四种cast操作符的区别--类型转换
- 对C和C++区别的一些零散总结
- C与C++之间的区别总结
- C++ 指针和引用区别的总结
- c++ 字符类型总结区别wchar_t,char,WCHAR
- C与C++的语法区别总结
- C++程序员进军Android系列:C++跟JAVA的区别总结
- C#与C++的区别总结
- C++ 字符类型总结&CString ,BSTR ,LPCTSTR之间关系和区别
- static在C和C++中的区别与总结