文章目录
#1. c++对c的扩展
变量定义方面:命名空间,不能重复定义全局变量,定义位置更灵活。
数据类型方面:struct,register,const,bool,并且变量和函数必须有类型。
另外还有三目运算符、等号等操作符的优化,以及函数扩展。
1.1 命名空间
引入namespace,更好地控制标识符的作用域,避免冲突。
c语言只有一个全局作用域,也叫默认命名空间,被所有全局标识符共享,可能发生冲突。
c++将全局作用域分成不同部分,不同namespace的标识符可以重名并不冲突。
命名空间可以相互嵌套。
定义
namespace ns1{
int foobar = 1;
}
使用namespace:using namespace ns1;
使用嵌套的命名空间:using namespace ns1::ns2
使用namespace中的变量:using ns1::foobar;
使用默认namespace中的变量:::foobar
,或者直接使用foobar
,::cout-->cout
若没有using namespace std,需要这样写:std::cout
,因为iostream没有引入std。
C++中可以同时using 两个命名空间,但前提是这两个空间中没有相同的变量或函数.
c++为了与c区分,规定头文件不使用后缀.h
iostream.h在include中是另一个文件,速度慢,c++标准已经不支持。
##1.2 变量定义位置
c++变量可以在需要时定义
1.3 register
register关键字优化,
c中,register修饰的变量不能取地址,c++可以。
取register变量地址时,register声明无效。
并且不使用register关键字也可以优化,比如循环中的index变量。
1.4 全局变量的检测
c中,定义重名的全局变量是合法的,它们会被链接到全局数据区的同一地址空间上。
c++则不允许重复定义全局变量。
int a;
int a = 1;
int main(){..}
这段代码,c不会报错,c++会报错。
1.5 struct
c中,struct并不被编译器当作类型。
c++中,struct是新类型的定义声明。并且能用访问修饰符。
struct Person{
public:
int age;
};
int main()
{
Person p;
return 0;
}
1.6 变量和函数必须有类型
类型检查不严格是c的缺陷。
func(i){
printf("1\n");
}
int main()
{
func(1,2,3);
return 0;
}
这段代码在c中不会报错并且会执行。
在c中,int f()
接收任意参数,int f(void)
无参;而c++中它们都是无参。
1.7 bool类型
理论上占1字节,多个bool定义在一起可能各占1bit,这取决于编译器的实现。
编译器 用0和1表示,输出也为1或0。
c中需要#include<stdbool.h>
或者
typedef enum{false = 0, true = 1} bool;
或者
typedef int bool;
#define true 1
#define false 0
或者
#define bool int
#define true 1
#define false 0
1.8 三目运算符
( a < b ? a : b ) = 3;
在c中,表达式不能做左值。
在c中,表达式的运算结果放在寄存器中。且表达式的返回值是一个具体的数
在c++中表达式返回的是变量自身,或者说返回一个内存空间(or内存的首地址or指针),c++编译器帮程序员完成了取地址的工作。
c中可以这样实现:
int c = 1;
int d = 2;
*( c < d ? &c : &d ) = 3;
1.9 关于const
const意味着只读
复习
int * const a;
//指针常量
const int * a;
//int常量
typedef struct {
int age;
} Person;
void f1(const Person * p){
//p->age = 1;
p = NULL;
}
void f2(Person * const p){
p->age = 1;
//p = NULL;
}
真正的常量
c中,const变量可以通过指针更改。
int main()
{
const int a = 1;
int *p = NULL;
p = (int*)&a;
printf("&a:%08X\n",&a);
cout<<"p :"<<p<<endl;
*p = 2;
printf("a:%d\n",a);
*(int*)&a = 3;
printf("a:%d\n",a);
//cout<<"*p :"<<*p<<endl;
return 0;
}
但在c++中,这段代码无效。因为c++不再给const变量单独分配内存,而是放在符号表(key:value)中。
当用指针取地址时,会单独分配内存。cout<<"*p :"<<*p<<endl会显示这块内存。
所以c++中的const变量才是真正的常量。
总结一下就是:
c中,const变量分配存储空间;
c++中,以下情况会分配空间:
- 使用&取const变量地址;
- const变量是全局变量,并被其它文件使用。
并且,c++的const变量分配内存是在编译期间。
int main()
{
int a;
const int b = 1;
int c;
cout<<&a<<endl;
cout<<&b<<endl;
cout<<&c<<endl;
cin.get();
return 0;
}
输出
0x66ff34
0x66ff30
0x66ff2c
可以证明分配顺序并不是a、c、b.
对比宏
const其实是c++替换c的#define的一种方法。
int a = 1;
int b = 2;
int c[a+b];
/*
linux内核的gcc支持;c/c++编译器不支持。
下面这样写则是支持的,由此代替了宏定义。
*/
const int a = 1;
const int b = 2;
int c[a+b];
const和宏定义都是编译期处理的。
它们的不同之处在于:
- const由编译器处理,提供类型检查和作用域检查;
- 宏定义由预处理器处理,是文本替换。
void f1()
{
#define a 1
const int b = 2;
#undef a
}
void f2()
{
printf("%d\n",a);
//printf("%d\n",b);
}
int main()
{
f2();
return 0;
}
常量成员函数
常量成员函数,不能通过this指针修改无mutable修饰的成员变量 。 编译器将对这些const成员函数进行检查, 如果确实没有修改对象值的行为,则检验通过。
1.10 函数扩展
c++对c的函数扩展有:
内联,默认参数,占位参数,函数重载
inline
常引用可以替代宏常数,而内联函数可以替代宏代码。
inline int func(){...}
inline关键字必须写在函数定义的地方,而不是声明的地方。
内联函数在最终生成的代码中是没有定义的,直接将函数体插入到调用处,也就没有了压栈、跳转、返回的开销。
宏代码是预处理器处理的,无编译过程;内联函数是由编译器处理的。
一般很短的代码才内联,如简单输出、三目运算这些可以define的操作。
没有inline关键字的简短函数也可能内联编译。
编译器接收inline请求的条件:
- 不能有循环,不能有过多条件判断。
- 不能有取址操作
也就是说编译器可以拒绝内联编译请求。
默认参数
int func(int arg = 1){...};
默认参数应在非默认参数的右边
传参时也可以传入实参改变默认参数
func(2)
占位参数
int func(int x , int){}
调用时必须传入占位参数。
占位参数没有形参名,函数体内无法使用。它的意义在于兼容C语言中的不规范写法,以及日后扩展。
一般结合默认参数
int func(int x , int=1){}
函数重载
相同函数名,不同参数,也就是说,函数重载由函数名和参数决定,而返回值及类型不是重载的因素。
void func(int a, int b, int c = 0){}
void func(int a, int b){}
func(1,2)
上面这段代码遇到默认参数,有二义性,编译不通过。
函数指针
#include <iostream>
using namespace std;
void func(char *s){
cout<<s<<endl;
return;
}
void func(char a,char b){
cout<<"two chars"<<endl;
//cout<<a<<endl;
return;
}
//声明一个函数类型
typedef void (myFuncType)(char *s);
//声明一个函数指针类型
typedef void (*myPFuncType)(char *);
typedef void (*myPFuncType2)(char,char);
int main(int argc, char *argv[])
{
myFuncType funcType;
myPFuncType pFuncType = NULL;
myPFuncType2 pFuncType2 = NULL;
pFuncType = func;
pFuncType2 = func;
pFuncType("pfunctype");
pFuncType2('a','b');
//定义一个函数指针变量
void (*pFunc)(char *s);
pFunc = func;
(*pFunc)("pfunc");
return 0;
}