C++语言总结(一)
C++语言总结(一)
C++语言是在C语言基础之上,添加了面向对象、模板等现代程序设计语言的特性而发展起来的。两者无论是从语法规则上,还是从运算符的数量和使用上,都非常相似。
C语言和C++并不是对立的竞争关系:
- C++是C语言的加强,是一种更好的C语言。
- C++是以C语言为基础,并且完全兼容C语言的特性。
在C语言中头文件使用扩展名.h,将其作为一种通过名称标识文件类型的简单方式。但是C++的用法改变了。C++头文件没有扩展名。但是有些C语言的头文件被转换为C++的头文件,这些文件被重新命名,丢掉了扩展名.h,并在文件名称前面加上前缀(表明来自C语言)。例如C++版本的math.h为cmath由于C使用不同的扩展名来表示不同的文件类型,因此用一些特殊的扩展名(如hpp或hxx)表示C++的文件也是可以的,ANSI/IOS标准委员会也认为是可以的,但是关键问题是用那个比较好,最后一致同意不适合任何扩展名。
头文件类型 | 约定 | 示例 | 说明 |
---|---|---|---|
C++旧式风格 | 以.h结尾 | iostream.h | C++程序可用 |
C旧式风格 | 以.h结尾 | math.h | C/C++程序可用 |
C++新式风格 | 无扩展名 | iostream | C++程序可用,使用namespace std |
转换后的C | 加上前缀C,无扩展名 | Cmath | C++程序可以,可使用非C特性,如namespace std |
命名空间namespace
在C++中,名称可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的C++库时,这些标识符的命名发生冲突,标准C++引入了关键字namespace,可以更好的控制标识符的作用域。
- 1
命名空间使用方法
创建一个命名空间
namespace A{
int a = 10;
}
namespace B{
int a = 20;
}
void test()
{
std::cout << "A::a : " << A::a << std::endl;
std::cout << "B::a : " << B::a << std::endl;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
命名空间只能在全局范围内定义
命名空间可嵌套命名空间
命名空间是开放的,即可以随时把新的成员加入到已有的命名空间中
声明和实现可以分开
无命名空间,意味着命名空间中的标识符只能在本文件内访问,相当于给这个标识符加上了static,使其可以作为内部链接
命名空间别名:namespace shortname = veryLongName
面向对象的三大特性
封装
把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
类将成员变量和成员函数封装在类的内部,根据需要设置访问权限,通过成员函数管理内部状态。
继承
集成所表达的类之间相关的关系,这种关系使得对象可以继承另外一类对象的特征和能力。
继承的作用:避免公用代码的重复开发,减少代码和数据冗余。
多态
多态性可以简单的概括为“一个接口,多种方法”,字面意思为多种形态,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。
Struct类型加强
c中定义结构体变量需要加上struct关键字,C++不需要
c中的结构体只能定义成员变量,不能定义成员函数,C++即可以定义成员变量,也可以定义成员函数
struct Student{
string name;
int mAge;
void setName(string name){mName = name;}
//C++既可以成员变量,也可以定义成员函数
void setAge(int age){mAge = age;}
void showStudent(){cout << "Name:" << mName << " Age:" << mAge << endl;}
};
void test01(){
Student student;//C++定义结构体时,不需要加struct关键字
student.setName("john");
student.setAge(20);
student.showStudent();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
C++具有更严格的类型转换,在C++中,不同类型的变量一般是不能直接赋值的,需要相应的强转。
在C语言中三目运算表达式返回值为数据值,为右值,不能赋值。
在C++语言中三目运算表达式返回值为变量本身,为左值,可以赋值。
(a > b ? a : b ) = 100;
c和c++中的const的区别
在c++中,一个const不必创建内存空间,而在c中,一个const总需要一块内存空间。而在C中,一个const总需要一块内存空间。在C++中,是否为const常量分配内存空间依赖于如何使用,一般来说,如果一个const仅仅用来把一个名字用一个值代替,那么该存储空间就不必创建。
如果存储空间没有分配内存,在进行完数据类型检查后,为了代码更加有效,值也许会折叠到代码中。
c语言全局const会被存储在只读数据段。C++中全局const当声明extern或者对变量取地址时,编译器会分配存储地址,变量存储在只读数据段,两个都受到了只读数据段的保护,不可修改。
C语言中局部const存储在堆栈区,只是不能通过变量直接修改const只读变量的值,但是可以跳过编译器的检查,通过指针间接修改const值。
在程序编写时,尽量使用const替换#define
const和#define区别总结
- 1.const有类型,可进行编译器类型安全检查。#define无类型,不可进行类型检查
- 2.const有作用域,而#define不重视作用域,默认定义处到文件结尾,如果定义在指定作用域下的有效常量,而#define就不能用
引用
引用是C++对c的重要扩充。在C/C++中指针的作用基本都是一样的,但是C++增加了另一种给函数传递地址的途径,这就是引用传递,它也存在于一些其他编程语言当中,并不是C++的发明。
- 变量名实质上是一段连续闪存空间的别名,是一个标号
- 程序中通过变量来申请并命名内存空间
- 通过变量的名字可以使用存储空间
Type &ref = val;
- &在此不是求地址运算,而是起标识作用
- 类型标识符是指目标变量的类型
- 必须在声明引用变量时进行初始化
- 引用初始化之后不能改变
- 不能有NULL引用,必须确保引用是和一块合法内存的存储单元关联
- 建立对数组的引用
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编辑内部实现,用户不可见。
引用的使用场景
常量引用主要应用在函数的形参,尤其是类的拷贝/复制构造函数
将函数的形参为常量引用的好处
- 引用不产生新的变量,减少形参与实参传递时的开销
- 由于引用可能导致实参随形参改变而改变。将其定义为常量引用可以消除这种副作用
内联函数
在C中我们经常把一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由是为了执行效率,宏可以避免函数调用的开销,这些都由预处理来完成。
在C++中,使用预处理会出现两个问题:
- 第一个在C中也会出现,宏看起来像一个函数调用,但是会有隐藏一些难以发现的错误
- 第二个问题是C++特有的,预处理器不允许访问类的成员,也就说预处理器不能用作类的成员函数
为了保持预处理宏的效率又增加安全性,而且还能像一般成员函数那样可以在类里访问自如,C++引入了内联函数(inline function)
内联函数为了继承宏函数的效率,没有函数调用时的开销,然后又可以像普通函数那样,可以进行参数,返回值类型的安全检查,又可以作为成员函数。
预处理宏的缺陷
#define ADD(x,y) x+y
inline int Add(int x,int y){
return x + y;
}
void test(){
int ret1 = ADD(10, 20) * 10; //希望的结果是300,展开为10 + 20 * 10;
int ret2 = Add(10, 20) * 10; //希望结果也是300
cout << "ret1:" << ret1 << endl; //210
cout << "ret2:" << ret2 << endl; //300
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
内联函数基本概念
在C++中,预定义的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。
在普通函数前面加上inline关键字使之成为内联函数,但是必须注意函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
内联函数的确占用空间,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压栈,跳转,返回的开销,我们可以理解为内联函数是以空间换时间。
为了定义内联函数,通常必须在函数定义前面放一个inline关键字。但是在类内部定义内联函数时并不是必须的,任何在类内部定义的函数自动成为内联函数。
在C++内部编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 不能对函数进行取址操作
内联仅仅是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。
函数的默认参数
C++在声明函数原型的时候可为一个或多个参数指定默认(缺省)的参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值代替。
- 函数的默认参数从左向右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须设置默认参数
- 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认参数。
函数的占位参数
C++在声明函数时,可以设置占位参数,占位参数只有参数类型声明,而没有参数名声明。一般情况下,在函数体内部无法使用占位参数,占位参数可以设置默认值
void func(int a, int b, int = 10);
函数重载
在传统C语言中,函数名必须是唯一的,程序中不允许出现同名的函数,在C++中允许出现同名函数,这种现象被称为函数重载,函数重载的目的是为了方便的使用函数名。
实现函数重载的条件
- 同一个作用域
- 参数个数不同
- 参数类型不同
- 参数顺序不同
函数返回值不能作为重载条件,因为在调用参数类型一样的函数时,编译器无法获取到返回值,所以无法确认调用那个函数。
在函数具有默认参数的时候,需要注意二义性问题。就是要考虑到默认值的问题。
编译器为了实现函数重载,使用不同参数类型来修饰不同的函数名。
extern “C”的作用就是为了实现C++代码能够调用其他C语言代码,加上extern “C”后,这部分代码编译器按C语言的方式进行编译和链接,而不是按C++的方式,这个可以防止C语言在C++编译时会出现的问题
#ifdef _cplusplus
extern "C"{
#endif
......
#ifdef _cplusplus
}
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-526ced5128.css" rel="stylesheet">
</div>
</article>
<div class="postTime">
<div class="article-bar-bottom">
<span class="time">
文章最后发布于: 2017-05-27 08:11:32 </span>
</div>
</div>