C++基础 —— 概述
C++概述
第一章 C++概述、简单知识点介绍
C语言和C++的联系
C++ 和C语言虽然是两门独立的语言,但是它们却有着扯也扯不清的关系。早期并没有“C++”这个名
字,而是叫做“带类的C”。“带类的C”是作为C语言的一个扩展和补充出现的,它增加了很多新的语法,目 的是提高开发效率。
这个时期的 C++ 非常粗糙,仅支持简单的面向对象编程,也没有自己的编译器,而是通过一个预处理程序(名字叫 cfront),先将 C++ 代码”翻译“为C语言代码,再通过C语言编译器合成最终的程序。随着 C++ 的流行,它的语法也越来越强大,已经能够很完善的支持面向过程编程、面向对象编程
(OOP)和泛型编程,几乎成了一门独立的语言,拥有了自己的编译方式。
C++ 拥有独立的编译器,例如 Windows 下的微软编译器(MSVC)、Linux 下的 GCC 编译器、Mac 下的 Clang 编译器,它们都同时支持C语言和 C++,统称为 C/C++ 编译器。对于C语言代码,它们按照C语言的方式来编译;对于 C++ 代码,就按照 C++ 的方式编译。
从表面上看,C、C++ 代码使用同一个编译器来编译,所以上面我们说“后期的 C++ 拥有了自己的编译方式”,而没有说“C++ 拥有了独立的编译器”。
从语法上看,C语言是 C++ 的一部分,C语言代码几乎不用修改就能够以 C++ 的方式编译。
- 从语法上来讲,C语言是C++的一部分,C语言代码几乎不用修改就能够用C++的方式编译。细致来讲,C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事务)控制)。
- C++,首要考虑的是如何构造一个对象模型,让这个模型能够契合与之对应的问题域,这样就可以通过获取对象的状态信息得到输出或实现过程(事务)控制。 所以C与C++的最大区别在于它们的用于解决问题的思想方法不一样。之所以说C++比C更先进,是因为“ 设计这个概念已经被融入到C++之中 ”。
头文件
C++为了兼容C,支持所有的C头文件,但为了符合C++标准,所有的C头文件都有一个C++版本的,
即去掉.h,并在名子前面加c。如和。
|C语言| C++|
|stdio.h |iostream|
|math.h |cmath|
|string.h |cstring|
|stdlib.h |cstdlib|
命名空间namespace
namespace是指标识符的各种可见范围。名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称互相冲突性的可能性越大。
假设这样一种情况,当一个班上有两个名叫 maye的学生时,为了明确区分它们,我们在使用名字之外,不得不使用一些额外的信息,比如他们的家庭住址,或者他们父母的名字等等。为了解决上输入问题,引入了命名空间这个概念,它可作为附加信息来区分不同库中相同名称 的函数、类、变量等。本质上,命名空间就是定义了一个范围。
注意:命名空间只能在全局内定义。
命名空间别名参照下面实例,using声明可以使得指定的标识符可用。
#include<iostream>
using namespace std;
//命名空间只能放在全局、也可以实现实现和定义分离
namespace A {
int a = 10;
// 命名空间可以嵌套命名空间
namespace B {
int a = 20;
}
void coutHello() {
cout << "hello"<<endl;
}
/*
coutHi不可用 如果命名空间包含一组重载的函数;
using声明就声明了这个重载函数的所有集合
*/
void coutHello() {
cout << "hello" << endl;
}
void coutHi() {
cout << "hi"<<endl;
}
}
namespace B {
int a = 100;
}
int main(){
cout << "A::a : " << A::a << endl;
cout << "A::B::a : " << A::B::a << endl;
namespace otherName = A;
cout << "otherName of A ::a :" << otherName::a << endl;
using A::coutHello;
coutHello();
}
using遇到重载函数时,声明了这个重载函数的所有集合。
#include<iostream>
using namespace std;
namespace A {
void func() {}
void func(int x) {}
int func(int x, int y) { return 1; }
}
int main() {
using A::func;
func();
func(10);
func(10, 20);
return 0;
}
输入输出
C语言的的输入输出用的主要是scanf()、printf()函数,而C++是使用类对象cin、cout进行输入输
出。
cin 输入流对象
cout 输出流对象
endl 换行,并清空输出缓冲区(end line 结束一行,并另起一行)
\n照样可以在cout中使用
基本数据类型
C++和C的基本数据类型几乎一样
值得注意的是,C语言中虽然也有bool(布尔类型),但是需要包含头文件<stdbool.h>,而在C++中则 不用,直接使用即可。
布尔类型对象可以被赋予文字值true或false,所对应的关系就是真与假的概念,即1,0。 可以使用boolalpha打印出bool类型的true或false
结构体加强
c中定义结构体变量需要加上struct关键字,然而c++不需要。c++既可以定义成员变量,也已定义成员函数。然而c能定义成员函数。
//1. 结构体中即可以定义成员变量,也可以定义成员函数
struct Student{
string mName;
int mAge;
void setName(string name){ mName = name; }
void setAge(int age){ mAge = age; }
void showStudent(){
cout << "Name:" << mName << " Age:" << mAge << endl;
}
};
//2. c++中定义结构体变量不需要加struct关键字
void test01(){
Student student;
student.setName("John");
student.setAge(20);
student.showStudent();
}
强弱类型
C语言:强类型,弱检查—— 一般就叫做弱类型了
C++:强类型,强检查 —— 真正意义上的强类型
强制转换
在C++,不同类型的变量一般是不能直接赋值的,需要相应的强转。
c代码
typedef enum COLOR{ GREEN, RED, YELLOW } color;
int main(){
color mycolor = GREEN;
mycolor = 10;
printf("mycolor:%d\n", mycolor);
char* p = malloc(10);
return EXIT_SUCCESS;
}
作用域运算符::
通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
//全局变量
#include<iostream>
using namespace std;
//全局变量
int a = 10;
int main(){
//局部变量
int a = 20;
//全局a被隐藏
cout << "a:" << a << endl;
}
这个例子可以看出,作用域运算符可以用来解决局部变量与全局变量的重名问题,即在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。
void* p
=
NULL;
2 int* p1 = p; //错误 “初始化”: 无法从“void ”转换为“int ”
3
4 int pn = NULL;
5 void pp = pn; //正确 任意类型的指针都可以自动转为万能指针
NULL和nullptr
NULL是给指针赋值的,表示指针指向的是空,nullptr 出现的目的是为了替代 NULL。
在C语言中NULL会被定义成(void*)NULL,但是C++不允许直接将 void * 隐式转换到其他类型,
NULL 只好被定义为 0。
const
C语言中的冒牌货 :C语言中的const并不是真正的常量,只是表示const修饰的变量为只读。
可以看到常量it的值已经通过指针被间接改变
明明已经通过指针修改了a值,为什么输出却没有变呢?
解释:
C++编译器当碰见常量声明时,在符号表中放入常量,那么如何解释取地址呢?
编译过程中若发现对const使用了&操作符,则给对应的常量分配存储空间(为了兼容C)
const参数不匹配的情况
变量的初始化
在C++中变量的初始化,又有了奇葩的操作(极度猥琐)
1,背景
在C++语言中,初始化与赋值并不是同一个概念:
初始化:创建变量时赋予其一个初始值。
赋值:把对象(已经创建)的当前值擦除,而用一个新值来代替。
2,列表初始化
作为C++11新标准的一部分,用花括号来初始化变量得到了全面应用(在此之前,只是在初始化数 组的时候用到)。列表初始化有两种形式,如下所示:
说明:上述的两种方式都可以将变量a初始化为0。
2.1 局限
当对内置类型使用列表初始化时,若初始值存在丢失的风险,编译将报错,如:
3,直接初始化
如果在新创建的变量右侧使用括号将初始值括住(不用等号),也可以达到初始化效果
三目运算符
c语言三目运算表达式返回值为数据值,为右值,不能赋值。 c++语言三目运算表达式返回值为变量本身(引用),为左值,可以赋值。
int a = 10;
int b = 20;
printf("ret:%d\n", a > b ? a : b);
//思考一个问题,(a > b ? a : b) 三目运算表达式返回的是什么?
cout << "b:" << b << endl;
//返回的是左值,变量的引用
(a > b ? a : b) = 100;//返回的是左值,变量的引用
cout << "b:" << b << endl;
左右值解释:
在c++中可以放在赋值操作符左边的是左值,可以放到赋值操作符右面的是右值。
有些变量即可以当左值,也可以当右值。
左值为Lvalue,L代表Location,表示内存可以寻址,可以赋值。
右值为Rvalue,R代表Read,就是可以知道它的值。
new/delete和malloc/free
在软件开发过程中,常常需要动态地分配和释放内存空间,例如对动态链表中结点的插入与删除。
在C语言中是利用库函数malloc和free来分配和释放内存空间的。C++提供了较简便而功能较强的运算符
new和delete来取代malloc和free函数。
注意: new和delete是运算符,不是函数,因此执行效率高。
new 和 malloc不要混用
分配内存使用完,记得释放内存(数组和普通变量释放有些微区别)
for循环
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。
因此C++中引入了基于范围的for循环,for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围
特点:
从数组的第一个元素开始,逐个赋值给迭代变量不依赖于下标元素,通用
函 数
内联函数
默认函数
定义函数时可以给形参指定一个默认的值,这样调用函数时如果没有给这个形参赋值(没有对应的 实参),那么就使用这个默认的值。也就是说,调用函数时可以省略有默认值的参数。如果用户指定了 参数的值,那么就使用用户指定的值,否则使用参数的默认值。
小结:
有函数声明时,默认参数可以放在声明或定义中,但不能同时存在
在具有多个参数的函数中指定默认值时,默认参数都必须出现在不默认参数的右边,一旦某个参 数开始指定默认值,它右边的所有参数都必须指定默认值.
也就是说,函数声明时,必须按照从右向左的顺序,依次给与默认值。
占位参数
定义函数时,还可以给函数提供占位参数
占位参数只有参数类型,而没有参数名在函数体内部无法使用占位参数
占位参数也可以指定默认参数
函数重载
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称 为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,对于程序的可 读性有很大的好处。
不同参数列表:
参数个数不同参数类型不同参数顺序不同
函数重载与返回值类型无关
函数重载可以根据具体的参数去决定调用哪一个函数。
为什么需要函数重载?
试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个maxmum函数取不同的名 字,如maxmum_int、maxmum_string等等。这里还只是简单的几种情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,这样做很不友好!
类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化 不同的对象,那是相当的麻烦!
操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于 连接字符串等!
重载函数的调用匹配规则
为了估计哪个重载函数最适合,需要依次按照下列规则来判断:
精确匹配:参数匹配而不做转换,或者只是做微不足道的转换,如数组名到指针、函数名到指 向函数的指针;
提升匹配:即整数提升(如bool 到 int、char到int、short 到int),float到double
使用标准转换匹配:如int 到double、double到int、double到long double、Derived到
Base、T到void、int到unsigned int;
编译器傻了:如果在最高层有多个匹配函数找到,调用将被拒绝(因为有歧义、模凌两可)
函数重载遇上默认参数
在给重载函数指定默认参数时,要考虑是否会和别的重载函数冲突
s tring
to_string(T val)
在C语言中,我们经常使用字符串,但要对字符串进行操作的时候,我们需要使用一些函数进行操 作,但也非常麻烦,那么在C++中有没有更好的字符串操作方法呢?
不用多说,肯定是有的,C++ 标准库提供了 string 类型,支持C语言字符串所有的操作,另外还增加了其他更多的功能。(现在我们可能还无法透彻地理解string,因为到目前为止我们还没有讨论类和对 象。所以现在粗略地了解下即可,等理解了面向对象的概念之后再回头来理解。)
注意:需要包含头文件#include和命名空间std