您的位置:首页 > 编程语言 > C语言/C++

C++对C语言的扩展

2016-04-28 09:32 639 查看

一、C++对C的扩展:

(1)C面向过程加工的是一个个函数,C++面向对象加工的是一个个类;

(2)为什么要写成员函数?

class MyCircle
{
public :
void setR(double d)
{
m_r = d;
}
/*double getS()               //成员函数
{
s = 3.14*m_r*m_r;
return s;
}*/
public:
double m_r;
double s = 3.14*m_r*m_r;     //直接赋值
//初始化时,m_r是个随机值,所以s也是一个随机值。当在类外直接
//调用mycircle.s时是直接去s的内存空间拿值,并没有也不会执行
//3.14*m_r*m_r,即使给m_r赋值,mycircle.s也仍然是个随机值。
};


(3)namespace(名字空间、作用域);

     所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。为了避免在大规模程序的设计中以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace(名字空间),可以更好地控制标识符的作用域。

   C中的命名空间:

      在C语言中只有一个全局作用域;C语言中所有的全局标识符共享同一个作用域;标识符之间可能发生冲突。  

   C++中提出了命名空间:

     命名空间将全局作用域分成不同的部分;不同命名空间中的标识符可以同名而不会发生冲突;命名空间可以相互嵌套。

namespace A
{
int a = 10;
namespace B
{
int a = 20;
}

}
{
cout << A::a << endl;     //10
//cout << B::a << endl;         //编译错误
cout << A::B::a << endl;  //20
//using namespace B;              //编译错误
//cout << a << endl;
using namespace A::B;
cout << a << endl;        //20

using namespace A;
//cout << a << endl;      //编译错误
cout << A::a << endl;     //10, using 关键字把名字空间里面的标识符全部暴漏
cout << B::a << endl;     //20

using namespace B;        //这时,上面的using namespace A已经将B“暴漏”,所以可以找到。
cout << B::a << endl;     //20
}


(4)实用性

     C语言的变量都必须在作用域开始的位置定义(VC6.0——》X.c)!C++中更强调语言的“实用性”,所有的变量可以在需要使用时再定义。

(5)struct类型功能加强

     C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型;C++中的struct是一个新类型的定义声明。

struct Student         //在C++中,struct和class完成的功能是一样的
{
char name[32];     //默认public,写不写无所谓
int age;
};

void main()
{
//struct Student stu;  //C语言Student stu;编译错误
Student stu;            //C++
stu.age = 10;
}


(6)C++中所有变量和函数都必须有类型

在C语言中:

 int f(    );表示返回值为int,接受任意参数的函数,     

         比如:f(1,2,3,4);

 int f(void);表示返回值为int的无参函数,

         比如:f();

在C++中:

 int f(  );和int f(void)具有相同的意义,都表示返回值为int的无参函数。C++更加强调类型,任意的程序元素都必须显示指明类型。

 

(7)C++新增bool类型关键字

    它只有0和1,没有其他值。非0都是1;

(8)三目运算符

C语言中的三目运算符返回的是变量值,不能作为左值使用;

              (a < b ? a : b )= 30;   //编译错误

     C++本质::*(a < b ?&a
: &b )= 30;

    C++中的三目运算符可直接返回变量本身,能作为左值使用,可以出现在程序的任何地方。(a > b ? a : b) = 30;直接编译通过。注意:可能三目运算符返回的值中如果有一个是常量值,则不能作为左值使用:(a < b ?1 :
b )= 30;

    当左值的条件:要有内存空间;C++编译器帮助程序员取了一个地址而已。

(9)const表现

  [b] http://blog.csdn.net/songshimvp1/article/details/50975332 [/b]

 

(10)引用

引用——别名

     1、引用必须初始化,引用作为函数参数声明时不进行初始化。

     2、引用做函数参数的好处:

       值传递,(1)有复制操作,如果是复杂数据类型,代价比较大;(2)因为复制,也就是说形参是副本,形参和实参是两个不同的变量,对形参进行修改后,对实参不起作用。

指针,引用是变量的别名,在一些场合可以代替指针,引用相对于指针来说有更好的可读性和实用性。

     3、引用本质:

      引用有自己的内存空间,而且和指针占的内存空间大小一样。而且引用是一个常量!——Type& name——》Type* const name

     4、当函数返回值为引用时,若返回栈变量,不能成为其它引用的初始值,不能作为左值使用。

       若返回静态变量或全局变量,可以成为其他引用的初始值,即可作为右值使用,也可作为左值使用,C++链式编程中,经常用到引用,运算符重载等。

int getA1()
{
int a;
a = 10;
return a;
}

//返回a的一个副本
int& getA2()
{
int a;  //如果是局部变量——栈上的引用
a = 10;
return a;
}

int* getA3()
{
int a;
a = 10;
return &a;
}

//变量是 静态变量 或者 全局变量
int j1()
{
static int a = 10;
a++;
return a;
}
int& j2()
{
static int a = 10;
a++;
return a;
}

//函数当左值
int g1()
{
static int b = 10;
b++;
return b;
}
int& g2()
{
static int b = 10;
b++;
cout << b << endl;
return b;
}

int main()
{
int a1 = getA1();

int a2 = getA2();
int &a3 = getA2();  //debug版本下,这个值不稳定。(函数中栈上的变量的内存空间被搞掉了)(多测试几次)

int a4 = j1();

int a5 = j2();
int &a6 = j2();

cout << "a1:" << a1 << ",a2:" << a2 << ",a3:" << a3 << endl;
cout << "a4:" << a4 << ",a5:" << a5 << ",a6:" << a6 << endl;

//g1() = 100;     //编译错误
g2() = 100;       //打印 11
cout << g2() << endl;  //打印101 101
return 0;
}


     5、指针引用

int m_value = 1;

void func(int *&p)      //p:  是指针的引用,main()方法里的 *pn,*p:是main()方法里的pn指向的内容。
{
p = &m_value;

// 也可以根据你的需求分配内存
p = new int;
*p = 5;
}

int main(int argc, char *argv[])
{
int n = 2;
int *pn = &n;
cout << *pn << endl;
func(pn);
cout << *pn <<endl;
return 0;
}

     6、常引用

const Type& name = var;const引用让变量拥有只读属性 ,不能再用name去修改var的值了。

Const int &a = 10;这种情况会单独为常量值分配内存,并将引用名作为这段空间的别名

普通引用相当于 int * const e;

const  int  &e 相当于
const int* const e  

 

 

(11)inline内联函数

C++中内联编译的限制:不能存在任何形式的循环语句;不能存在过多的条件判断语句;函数体不能过于庞大;不能对函数进行取址操作;函数内联声明必须在调用语句之前;

编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。

内联函数在最终生成的代码中是没有定义的,C++编译器直接将函数体插入在函数调用的地方,内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)。inline只是一种请求,C++编译器不一定准许函数的内联请求!

内联函数由编译器处理,直接将编译后的函数体插入调用的地方;宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程。

一些函数即使没有inline声明,也可能被编译器内联编译。

二、函数重载:

判断标准:相同的函数名搭配不同的参数(参数的个数不同、参数的类型不同、参数的顺序不同),函数返回值不是判断函数重载的标准。

编译器调用重载函数的准则:将所有同名函数作为候选者;尝试寻找可行的候选函数;精确匹配实参;通过默认参数能够匹配实参;通过默认类型转换匹配实参;

匹配失败:最终寻找到的可行候选函数不唯一,则出现二义性,编译失败。无法匹配所有候选者,函数未定义,编译失败。

函数重载的注意事项:重载函数在本质上是相互独立的不同函数(静态链编);函数返回值不能作为函数重载的依据;函数重载是由函数名和参数列表决定的。

   A.函数默认参数 和 函数重载结合在一起

int f(int aa, int b,int c=0)    //默认函数参数
{
return aa + b;
}

int f(int aa, int b)
{
return aa + b;
}


f(1, 2);=》当函数默认参数 和 函数重载结合在一起时,会发生二义性错误,无法匹配。


   B.函数重载 和 函数指针 结合

//函数重载
int func(int x)
{
return x;
}

int func(int a, int b)
{
return a + b;
}

int func(const char* s)
{
return strlen(s);
}

//函数指针
typedef int(*PFUNC)(int); // int(int a)

int main()
{
int c = 0;
PFUNC p = NULL;   //定义一个函数指针 变量 ,用来指向函数的入口地址
p = func;
c = p(1);       //调用函数指针变量
printf("c = %d\n", c);
return 0;
}


因为函数指针在声明时,限定了参数类型和参数个数,所以在调用函数指针变量时会严格检查!

   C.杂

(1)普通函数形参为非引用类型,非指针类型,形参一个带const,一个不带const

1 void print(int x);

2 void print(const int x); //不算重载,直接报错重定义

 

(2)普通函数形参为引用类型或指针类型,一个形参带const,一个不带const

1 void print(int *x);

2 void print(const int *x); //算重载,执行正确,实参为const int *时候调用这个,为int*的时候调用上面一个

3 void print(int &x);

4 void print(const int &x); //算重载,执行正确,实参为const int &时候调用这个,为int&的时候调用上面一个

 

(3)类的成员函数,形参完全相同,一个函数为const成员函数,一个函数为普通成员函数

1 void print();

2 void print() const; //算重载。const对象或const引用或const指针调用时调用这个函数,普通对象或普通引用调用时调用上面一个。

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ c语言 扩展