- 从C到C++
C++基本情况介绍
++ C语言运算符 C++是在C语言之上的扩展
起初的名称:C with Class 即带类的C语言,只是C语言的一部分
-
C++的诞生时间
C++ 是由 Bjarne Stroustrup(本贾尼·斯特劳斯特鲁普 ) 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的C(C with Class),后来在 1983 年更名为 C++。
C++的标准化工作于1989年开始。
1998年,C++的ANSI/IS0标准被投入使用。
时间 标准版本 1989/1998 89年制定98年投入使用 2003 第二个C++标注,对98标准略作修改 2011 第三个C++标准,颠覆性的标准 2014 每三年提出一个标注 C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
-
C++的特点
- C++宏观上面向对象,微观上也面向过程,C语言为面向过程的语言
- 支持运算符重载(令自定义的类型也支持运算符(算数运算符,逻辑运算符等)
- 支持异常机制——错误处理(errno) #include
- 支持泛型编程——对类型抽象化
-
C++的应用
- 兼顾执行效率和开发效率
- C语言具有其他高级语言无法比拟的高效率
- C++有优化编译器,在部分场合能让C++具有C的执行效率
- 面向对象有丰富的扩展库
- 科学计算库 (基本上由C++实现
- 计算机网络 (ACE库
- 广泛引用与网络编程
- 网络游戏 (LOL等大型网络游戏)
- 强建模能力 (数字建模 3D
- 桌面应用程序 (office, windows桌面
- 驱动程序 (C++优化编译器使得其执行效率提高
- 兼顾执行效率和开发效率
-
C和C++的区别
- C++基本完全兼容C语言
- C语言面向过程,C++面向对象(宏观上面向对象,微观上也面向过程)
- 其他可见C++特点
- C++的函数支持重载、默认值、哑元
- C++的枚举是一种强类型
-
第一个C++程序!
#include
//C++标准IO库,一般C++的库基本没有.h后缀 //C++中可以直接使用C 语言的头文件,但是如果使用C语言标准的头文件,一般来说C++也有自己提供的 #include #include using namespace std;//C++ 把标准库中的对象类型的名称放在了std中。 int main() { cout<<"HelloWorld!"< 使用g++编译器 在文件名称后加 -lstdc++也可以用gcc编译器
后缀名一般为cpp 写成C,c++,cc也行
-
独特的基本数据类型
- C++中有bool布尔类型
- bool类型的取值为 true 和 false 表示 真和假 底层用 1和0存储
- 在输出时,true – 1 false—0 可以使用boolalpha 输出 true 和 false
- C++中有bool布尔类型
-
C++的struct类型
与C语言中struct的区别:
-
C++定义完结构体变量后,定义变量可以不使用struct关键字
-
C语言sizeof(空结构体)的大小为0,C++中为1,主要是为了标识内存
-
C++的struct中可以定义函数(在自定义类型中定义函数,即面向对象的思想)
在其中定义的函数可以直接访问结构体中的变量
结构体中的变量可以用static修饰,但需要在结构体外进行定义
类型 结构体名::成员名 = init_val;
C++的结构体可以用访问控制属性,(public、protected、private)
允许继承
-
-
C++的enum
是一种独立的类型,不能和C语言一样直接用数字赋值枚举类型的变量,只能用枚举值
但是可以用枚举给int类型变量赋值
enum Direction{UP,DOWN,LEFT,RIGHT}; Direction d = UP;//cout << d << endl; 依然是整数 d = DOWN; d = LEFT; d = RIGHT; d = 0; d = 1; d = 1024; //错误的 但是C语言中允许 int num = d; num = LEFT; //可以的 -
C++的union
- C++中支持匿名联合(既没有类型,也没有定义联合变量)
- C++中的匿名联合表示的是变量在内存中的布局方式
- 可以直接访问匿名联合中的属性
//以编译时,以联合的形式编译布局a,b,c,即让a,b,c共用一块内存 union{ int a; int b; char c; }; a = 1024; b = 9527; c = 'A'; -
C++的string
//保留了C风格的字符串,同时引入了string类型的字符串 string s1; //空字符串 string s2 = "Hello world"; //C指针的初始化方式 string s3("Hello world"); string s4 = {"HEllo world"}; string s5 = s4; string s6(10,'X'); //"XXXXXXXXXX" //指针是一种天然的迭代器 string s7(iterator begin,iterator end); //[begin,end) 用这个区间中的字符构造字符串
string变量的赋值
string s = "Hello world";
s = "中国速度"; //对string变量进行赋值
string s1("hello");
s1 = s; //字符串之间进行赋值
-
字符串的拼接 +
-
字符串中字符的访问与修改 []
-
字符串长度
size_t length(); s.length()
-
返回C风格的字符串
char *c_str();
-
字符串判断相等 ==
string a("Hello"),b("Hello"); if(a == b){//字符串a和字符串b相等 } -
元素访问
string s("Hello world"); //1.下标 [] cout << s[0] << endl; s[1] = 'E'; //2.at 对比[] 进行越界检查 如果越界会抛出异常 一旦越界,会抛出 std::out_of_range 异常 char& at(size_t index); cout << s.at(0) << endl; //s[0] //3.front C++11 g++ -std=c++0x char& front() 获取首字符 返回其引用 //4.back char& back() 获取末尾字符 不是 //5.data() const char* data(); //6.c_str() const char *c_str(); -
迭代器(指针)
正向迭代器 string::iterator it iterator begin(); //正向迭代器 字符串首字符的地址 iterator end(); //正向迭代器 字符串最后一个字符的下一个地址 逆向迭代器 reverse_iterator rbegin() //逆向迭代器 字符串最后一个字符的地址 reverse_iterator rend() //逆向迭代器 字符串第一个字符的前一个地址 //不能通过迭代器修改字符串中的内容 常正向迭代器 const_iterator cbegin() cend() 常逆向迭代器 const_reverse_iterator crbegin() crend() 正向迭代器 ++ 往后偏移 -- 往前偏移 逆向迭代器 ++ 往前偏移 -- 往后偏移 *迭代器 就相当于指针解引用-
字符数量和容量
bool empty() //检查字符串是否为空 size_t size(); //字符数量 size_t lenght(); //字符数量 void reserve(size_t s); //预分配内存 如果s小于目前的容量则不会干任何事件 只扩不缩 size_t capacity() //容量 自动扩容 ~~~
-
操作
void clear(); //指定位置 basic_string& insert( size_type index, size_type count, CharT ch );//在index位置插入count个ch字符 basic_string& insert( size_type index, const CharT* s ); //在index位置插入一个C风格的字符串 basic_string& insert( size_type index, const basic_string& str, size_type index_str, size_type count );//在index位置插入 str字符串 index_str开始往后count个字符 //指定迭代器 iterator insert( iterator pos, CharT ch ); void insert( iterator pos, size_type count, CharT ch ); void insert( iterator pos, InputIt first, InputIt last ); basic_string& erase( size_type index = 0, size_type count = npos );//从index处开始移除,移除count个字符,如果没有传递count,则移除至末尾 iterator erase( iterator position ); //移除指定位置(迭代器)的元素 iterator erase( iterator first, iterator last );//移除[first,last)区间的元素 void push_back( CharT ch ); //自动扩容 void pop_back(); //追加若干个字符 basic_string& append( size_type count, CharT ch ); basic_string& append( const basic_string& str );//追加一个字符串 basic_string& append( const basic_string& str,size_type pos, size_type count = npos ); int compare( const basic_string& str ) const; 支持== basic_string& replace( size_type pos, size_type count,const basic_string& str ); basic_string substr( size_type pos = 0, size_type count = npos ) const; constexpr basic_string substr( size_type pos = 0, size_type count = npos ) const; //如果没有找到str 则返回 npos 从pos位置开始查找 size_type find( const basic_string& str, size_type pos = 0 ) const;
-
名字空间其实是对代码的逻辑划分,避免名字冲突
-
名字空间的定义
namespace 名字空间名{ 定义全局变量; 定义函数; 定义类型; } -
使用名字空间中的内容 作用域限定符 ::
名字空间::标识符 ---指定使用哪个名字空间中的内容
-
声明名字空间对当前作用域可见
using namespace 名字空间名; 直接使用标识符 不需要 名字空间::标识符
- 如果使用using namespace声明多个名字空间,如果使用同名的标识符,会引发冲突,引发冲突之后,必须使用 名字空间::标识符
-
把名字空间中的标识符导入到当前作用域
using namespace::标识符 优先级会比using namespace 名字空间 优先级高 局部优先原则
-
名字空间名相同的名字空间 自动合并
- 名字空间名不会有重定义的问题
namespace N1{ int a; } namespace N1{ int b; } -
名字空间嵌套
-
匿名名字空间
-
C++会把声明定义在全局区的标识符通通归纳到一个统一的名字空间中,称为匿名名字空间
-
直接使用 ::标识符 指定访问全局域中的标识符 (匿名名字空间中的标识符)
int g = 1024; int main(){ int g = 9527; cout << g << endl; { extern int g; cout << g << endl; } cout << ::g << endl; return 0; } -
多文件编程中
-
声明
namespace N{ extern int a; void func(); struct Stu{ int no; string name; }; } -
定义实现
int N::a; void N::func(){ }
-
-
-
- g++编译器不会再为函数提供隐式函数声明
- C++调用函数之前,必先声明或定义
- C语言函数的隐式声明:
- gcc编译器在编译代码时,如果遇到一个调用的函数在此之前没有定义和声明,则为其隐式声明一个函数,且为其声明函数的返回值类型为int
- C++函数的形参列表为空 就相当于 C语言中 形参列表为void
-
函数重载: 即在同一个作用域下,可以定义同名的函数,但函数的参数列表不同相当
- 同一个作用域下,函数名相同,参数列表不同即构成重载,在编译调用重载的函数时,会根据实参的个数和类型来绑定调用的函数,静态重载
- 前提条件:同一个作用域下 如果在不同的作用域,不能构成重载
- 必要条件:函数名相同,函数列表不同
- 函数列表不同
- 参数个数不同
- 同位置的类型不同
- 对于指针和引用的类型,常属性不同也能构成重载
- 如果是非指针和引用,常版本和非常版本会重定义
- 重载与形参名和返回值类型无关
- 函数列表不同
-
为什么C++支持重载,而C语言不支持重载
-
g++编译在编译函数时,会对函数进行改名操作 _Zn函数名形参类型简写 _Z3maxii _Z3maxdd _Z3maxiii
-
gcc编译器不会对函数名进行更名操作
-
可以用 extern “C” 来指定让g++使用C风格的方式来编译函数
extern "C" //如果需要在C语言中调用C++的函数,那就必须使用extern "C"来编译C++的代码 void func(){} extern "C"{ void f1(){}; void f2(){} }
-
-
注意调用重载时产生的二义性 (歧义)
void func(char a,char b){ } void func(int a,int b){ } int main(){ func('a','b');//有明确类型 没有任何歧义 func(1,2); //没有歧义 //func(3.14,5.16); //歧义 double->char double->int //func('a',5); //歧义 char,char int,int func('a',3.14); //没有歧义 char,char 只需要有一个类型隐式转换 func(3.14,5); //int,int return 0; }
-
在定义函数时,形参可以声明默认值
- 好像定义变量直接初始化一样
-
如果一个函数有默认值,则在调用该函数时,可以为该形参传递实参,也可以选择不传,不传则采用默认值
-
靠右原则
- 如果一个形参有默认值,则其后面(右边)所有的形参都必要要有默认值
-
有缺省值注意不要和重载产生歧义(二义性)
//_Z3barv void bar(){ } //_Z3barii void bar(int a=1,int b=2){ } //bar();//一调用则产生歧义 bar(1024); bar(9527); -
如果在函数声明和定义分离,则缺省值只能出现在函数声明中
void func(int a=1024); void func(int a){ }
-
只有类型,没有形参名谓之哑元
void func(int){ }- 在调用哑元函数时,一定要传递一个该类型的实参 只关心类型,不关心值的大小
- 需要注意和重载的重定义
- inline
- 优化方式 提高代码的运行效率
- 直接用函数的二进制指定替换函数的调用指令 加快程序执行速度 使可执行程序变大
- 只是一种申请和建议 取决于编译器
- 简单且重复调用的函数可以声明为内联函数



