关于C语言下的封装和C++语言封装的区别
1.封装
我们编写程序来解决现实中的问题,而这些问题的是由各种事物组成的。我们解决这个问题的前提就是将事和物抽象得到计算机程序中,也就是用程序语言表示现实的事物。
现实世界的事物所具有的共性就是每个事物都具有自身的属性和行为,所以如果我们能把这些事物的属性和行为表示出来,那么就可以抽象出这个事物。
属性和行为应该放在一起,一起来表示一个具有属性和行为的对象。为什么需要这样做呢?我稍后解释。
假如某对象的某项属性不想被外界获知,比如女孩子的体重,那么对于体重这个属性,就应该是只有女孩自己知道的属性,或者女孩的某些行为,比如暗恋某个男生,不想让外界知道,只能让自己知道,那么在这种情况下,封装应该提供一种机制,能够给属性和行为的访问权限控制住。
所以说封装特性包含两方面,一个是属性和变量合成一个整体,一个是给属性和函数增加访问权限。
封装
访问权限
函数默认参数及占位参数
1.函数默认参数
C++在声明函数原型时可为一个或多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值代替。
#include <iostream>
using namespace std;
void func01(int a = 10, int b = 100)
{
cout << "a+b=" << a + b << endl;
}
void func02(int a, int b = 12, int c = 11)
{
cout << "a=" << a << ",b=" << b << ",c=" << c << endl;
}
void func03(int a = 0, int b = 0);
void func03(int a, int b)
{
cout << "a=" << a << ",b=" << b << endl;
}
int main()
{
func01();//使用默认参数
func01(100, 100);//使用现在传入的参数
func01(110);//只传入一个参数,第二个参数使用默认参数
func02(12);//b,c使用默认参数
func03();//没有传参数,使用默认参数
return 0;
}
输出结果如下:
注意点:
2.函数的占位符
C++在声明函数时,可以使用占位参数。占位参数只有类型声明,而没有参数名声明。一般情况下,在函数体内无法使用占位参数。
#include <iostream>
using namespace std;
void func01(int a,int b,int)
{
//函数内部无法使用占位参数
cout << a + b << endl;
}
void func02(int a, int b, int = 10)
{
//无法使用占位参数
cout << a + b << endl;
}
int main()
{
func01(10, 15, 20);
func02(10, 25);//占位参数使用默认参数
return 0;
}
输出结果如下:
C++复合类型
——————————-
写在前面
C++从C中继承的一个重要特征就是效率。
在C中我们经常把一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由方式为了执行效率,宏可以避免函数调用的开销,这些都由预处理来完成。
但是在C++出现以后,使用宏处理会出现两个问题:
为了保持预处理宏的效率又增加安全性,而且能像一般成员函数那样在类里访问自如,C++引入了内联函数。
内联函数为了继承宏函数的效率,没有函数调用的开销,然后又可以像普通函数那样,可以进行参数、返回值类型的安全检查,又可以作为成员函数。
1.预处理宏的缺陷
预处理器宏存在问题的关键是我们可能认为预处理器的行为和编译器时一样的。当然也是由于宏函数调用和函数调用在外表看起来是一样的,因此也容易混淆。但是其中会有一些微妙的问题出现。
#include <iostream>
using namespace std;
#define ADD(x,y) x+y
void test()
{
int ret = ADD(10, 10);
int ret0 = ADD(10, 10) * 10;//预期结果为200
cout << "ret=" << ret << endl;
cout << "ret0=" << ret0 << endl;
}
int main()
{
test();
}
输出结果如下:
在输出结果里,我们可以看到,实际输出结果为110,并不是我们想要的结果。它实际进行运算时,运算式应该是:10+10*10。
对于这个微小的问题,我们可以通过加括号的方式来解决它。源代码和输出结果如下:
#include <iostream>
using namespace std;
#define ADD(x,y) ((x)+(y))
void test()
{
int ret = ADD(10, 10);
int ret0 = ADD(10, 10) * 10;//预期结果为200
cout << "ret=" << ret << endl;
cout << "ret0=" << ret0 << endl;
}
int main()
{
test();
}
输出结果如下:
我们可以看到,加括号之后,输出结果和预期相同。
为了防止出现例1中的问题,这次,我特意对三目运算符加了括号。代码如下:
#include <iostream>
using namespace std;
#define compare(a,b) ((a)<(b))?(a):(b)
void test()
{
int a = 10;
int b = 20;
int ret = compare(a, b);
cout << "ret=" << ret << endl;
int ret0 = compare(++a, b);
cout << "ret0=" << ret0 << endl;
}
int main()
{
test();
}
输出结果如下:
这次出现的问题让人有点摸不到头脑,因此++运算符表示在进行运算之前加1,但是输出结果中,实际输出为12,相当于加了两次。
除此之外,我们还应当注意的是,预定义宏函数没有作用域概念,无法作为一个类的成员函数,也就是说,预定义宏没办法表示类的范围。
2.内联函数
在C++中,预定义宏额概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当得到地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。
普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意函数体和声明结合在一起,否则编译器会把它当成普通函数对待。
inline void func(int a);
上面的这种写法没有任何效果,仅仅是声明函数,应该像下面这样的方式来定义。
inline void func(int a)
{
return ++;
}
注意:编译器将会检查函数参数列表使用是否正确,并返回值(进行必要的转换)。这些事预处理器是无法完成的。
内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用是否的压栈、跳转、返回的开销。我们可以理解为内联函数是以空间换时间。
内联说明只是向编译器发出一个请求,编译器可以选择忽略这个请求。
一般来说,内联机制用于优化规模较小、流程直接、频繁调用的函数。很多编译器都不支持内联函数递归函数,而且一个75行的函数也不太可能在调用点内联地展开。
写在前面
有时候我们希望定义这样一种变量,它的值不能被改变。例如,用一个变量表示缓冲区的大小。使用变量的好处是我们觉得缓冲区不再合适的时候,很容易对其进行调整。另一方面,也应随时警惕,防止程序一不小心修改了这个值。为了满足这一要求,可以用关键词const对变量的类型加以限定。