c++ 与c的差异
1. QT中文乱码问题
工具 -- 选项 -- 行为 -- 文件编码改为system
注意:
- 修改后新项目中文才不会乱码,如果是原有项目需重建 。
2. 输出
语法:
cout << 输出内容1 << 输出内容2 << ... << endl;
注意:
输出的内容中
endl
表示为换行c语言的输出是
stdio.h
中提供了printf
函数进行标准输出
printf("输出的格式",值1,值2,值3,...);
c++的输出是
iostream
中提供的cout
变量进行标准输出
cout << 值1 << 值2 << 值3...endl;
示例:
#include <iostream>using namespace std;int main(int argc, char *argv[])
{int a = 666;cout << "a的值是:" << a << endl;return 0;
}//a=10
3. 输入
语法:
cin >> 变量名1 >> 变量名2 >> ...
示例:
#include <iostream>using namespace std;int main(int argc, char *argv[])
{cout << "请输入一句话:" << endl;char str[30];
// cin >> str;
// cout << "str= " << str << endl;cin.getline(str, sizeof(str));cout << "str= " << str << endl;return 0;
}
注意:
直接使用
cin
,不能有空格、换行。要是用
cin.getline(str,sizeof(str));
c语言的输入
stdio.h
提供了
scanf("格式",存储输入的数据的地址); int num = 10; scanf("%d", &num);
c++的输入
iostream
,提供了cin
。
4. 作用域运算符
通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
符号:
::
作用:作用域运算符可以用来解决局部变量与全局变量的重名问题,即 在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。
示例:
#include <iostream>using namespace std;int x = 100;
int main(int argc, char *argv[])
{int x = 1;cout << "x =" << x << endl;cout << "::x =" << ::x <<endl;return 0;
}
//x =1
//::x =100
5. 命名空间
在 c++中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称互相冲突性的可能性越大。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的 c++库时,这些标识符的命名发生冲突,标准 c++引入关键字 namespace(命名空间/名字空间/名称空间),可以 更好地控制标识符的作用域。
5.1 定义
作用:封装多个变量、函数等,更好地控制标识符的作用域。
关键字:namespace
语法:
namespace [名称] {成员的声明,可以具有初始化的值;[ 内部嵌套 namespace {};] [声明成员函数][定义成员函数] } // 实现namespace中声明的函数 函数返回值类型 命名空间的名称::声明的函数名(形参列表) {}比如: namespace A{int num = 10;void test01();namespace B{int num02;} }
注意:
- 命名空间不能定义在函数中,只能定义在全局区域
示例:
#include <iostream>using namespace std;namespace A {int num01 = 10;void test01(){cout << "test01进来了" << endl;}void test02();//当前命名空间中 test02这个函数只声明了,没有实现namespace B {int num02;}
}//实现A命名空间中的test02函数
void A::test02()
{cout << "test02进来了" << endl;
}int main(int argc, char *argv[])
{//命名空间名::变量名或函数名或嵌套的命名空间名称cout << "A::num01=" << A::num01 << endl;cout << "A::B::num02=" << A::B::num02 << endl;A::test01();A::test02();return 0;
}//A::num01:10
//A::B::num02:0
//test01进来了
//test02进来了
5.2 注意
5.2.1 命名空间只能在全局区域定义(命名空间不能定义在函数中)
示例:
#include <iostream>using namespace std;
int main(int argc, char *argv[])
{ namespace A {int a = 10;} cout << A::a << endl;return 0;
}
//此时报错
5.2.2 命名空间嵌套命名空间
命名空间中可以嵌套命名空间
示例:
#include <iostream>using namespace std;
namespace A {int a = 10;namespace B {int b = 20;}
}
int main(int argc, char *argv[])
{ cout << "A::a" <<A::a << endl;cout << "A::B::b" << A::B::b << endl;return 0;
}
//10
//20
5.2.3 命名空间的开放性
随时添加新的成员
示例:
#include <iostream>using namespace std;namespace A {int num01 = 10;
}//命名空间A中再次定义一个变量num02
namespace A {int num02 = 10000;
}int main(int argc, char *argv[])
{cout << "A::num01=" << A::num01 << endl; //A::num01=10cout << "A::num02=" << A::num02 << endl; //A::num02=10000return 0;
}
5.2.4 无名命名空间
无名命名空间,意味着命名空间中的标识符 只能在本文件内访问
示例:无名命名空间中的内容 当成员使用,不用加 空间名称::
。
#include <iostream>using namespace std;namespace {int num03 = 99999;
}int main(int argc, char *argv[])
{cout << "num03=" << num03 << endl; //num03=99999return 0;
}
5.2.5 取别名
示例:
#include <iostream>using namespace std;namespace XXXXXXXXXXXXXXXXX {int num04 = 6666;
}int main(int argc, char *argv[])
{//给 命名空间取别名namespace B = XXXXXXXXXXXXXXXXX;cout << "B::num04=" << B::num04 << endl; //B::num04=6666return 0;
}
5.3 using 关键字作用
5.3.1 using声明命名空间中的具体成员
语法:using 命名空间名 :: 变量名
示例:
#include <iostream>using namespace std;namespace A{
int a = 10;
int b = 1;
}
int main(int argc, char *argv[])
{//声明命名空间A中的a变量using A::a;//使用时可以省略空间名::cout << "a=" <<a << endl; //a=10cout << "A::b=" << A::b << endl; //A::b=1return 0;
}
注意:
- 容易造成同范围内的命名冲突
#include <iostream>using namespace std;namespace A{int a = 10;int b = 1;
}
int main(int argc, char *argv[])
{//声明命名空间A中的a变量using A::a;int a = 200; //命名冲突,报错//使用时可以省略空间名::cout << "a=" << a << endl; //a=10cout << "A::b=" << A::b << endl; //A::b=1return 0;
}
5.3.2 using声明成员函数 遇到函数重载
#include <iostream>using namespace std;namespace A{int a = 10;int b = 1;void test(){cout << "test01" << endl;}void test(int x){cout << "test02" << endl;}void test(int x,int y){cout << "test03" << endl;}
}int main(int argc, char *argv[])
{//声明命名空间A中的所有test函数using A::test;test();test(10);test(1,2);return 0;
}//test01
//test02
//test03
5.3.3 声明整个命名空间
#include <iostream>using namespace std;namespace A{int a = 10;int b = 1;void test(){cout << "test01" << endl;}void test(int x){cout << "test02" << endl;}void test(int x,int y){cout << "test03" << endl;}
}//声明整个命名空间
using namespace A;int main(int argc, char *argv[])
{cout << "a=" << a << endl;cout << "b=" << b << endl;test();test(10);test(1,2);return 0;
}
//a=10
//b=1
//test01
//test02
//test03
6. 全局变量类型检测增强
#include <iostream>using namespace std;int a = 10; //赋值并定义
int a; //c语言认为是声明,通过,c++认为是定义,报错int main(int argc, char *argv[])
{cout << "Hello World!" << endl;return 0;
}
7. c++中所有的变量和函数都必须有类型
-
c 在自定义函数时,形参变量可以没有数据类型,即为任意类型,警告但是可以编译通过,并可以运行
-
c++中,函数的 形参变量必须指定类型。没有形参建议写void
c 语言代码:
#include <stdio.h>
//c语言中形参可以没有数据类型,此时形参可以为任意类型
//c++中形参必须有类型,没有形参建议写void
void fun(i)
{ printf("%d\n",i);
}
int main(int argc, char const *argv[])
{ fun(10);return 0;
}
c++ 函数形参没有数据类型,会报错
8. 更严格的类型转换
在c++中,不同类型的变量之间赋值时,需要明确的类型转换。基本类型小转大除外
c 语言代码:动态开辟内存,在c中不强转没问题
#include <stdio.h>int main(int argc, char *argv[])
{// 按c的方式自动转换int a = 65;char b = a;printf("%c\n",b); //A// 在c中没有问题char *p = malloc(32);strcpy(p, "jerry");return 0;return 0;
}
c++代码:动态开辟内存,在c++中必须强转,不然会报错
#include <iostream>
#include <cstdlib>
#include <cstring>using namespace std;int main(int argc, char *argv[])
{// 按c的方式自动转换int a = 65;char b = a;cout << "b=" << b << endl;// c++必须强转(明确数据类型),c语言可以不用char *p = (char *)malloc(32);strcpy(p, "jerry");cout << "p=" << p << endl;return 0;
}
9.struct类型加强
- c 中定义
结构体变量
需要加上 struct 关键字, c++不需要。 - c 中的结构体只能定义成员变量,不能定义成员函数。
c++
既可以定义成员变量
,也可以定义成员函数。
示例1:结构体变量不需要加 struct
#include <iostream>
using namespace std;
struct Stu
{ int id;char name[30];
};
int main(int argc, char const *argv[])
{ //c语言的写法//struct Stu s1 = {18,"张三"};//c++中定义结构体变量可以省略struct不写Stu s = {1, "jerry"};cout << "id=" << s.id << ", name=" << s.name << endl;return 0;
}
示例2:struct类型中可以有成员函数
#include <iostream>using namespace std;
struct Stu
{int age;char name[40];//c语言中结构体中不能定义函数//c++对其进行增强,使其可以定义函数void eat(){cout << name << "干饭" << endl;}
};
int main(int argc, char *argv[])
{cout << "Hello World!" << endl;s1.eat(); //张三干饭return 0;
}
10. 新增bool类型关键字
- 标准c++的bool 类型有两种内建的常量 true(转换为整数1) 和 false(转换为整数0);
- 非标准c++ 的 bool类型有两种内建的常量 true(转换为非0) 和 false(转换为整数0);
- 占1字节。
示例:
#include <iostream>using namespace std;int main(int argc, char *argv[])
{bool b1 = true;bool b2 = false;cout << "b1 = " << b1 << endl;cout << "b2 = " << b2 << endl;cout << "sizeof(bool)" << sizeof(bool) << endl;return 0;
}//b1 = 1
//b2 = 0
//sizeof(bool)1
11. 三目运算符功能增强
- c中三目运算表达返回是变量的
值
, - 而c++中,三目运算表达式返回是
变量(地址)
示例:
#include <iostream>using namespace std;int main(int argc, char *argv[])
{int a = 10;int b = 20;//c语言编译失败//c++成功/*c取的是a的值,所以以下代码在c中报错c++取的是a的地址,在c++成立*/(a>b?a:b) = 100;cout << "a=" << a << endl; //a=10cout << "b=" << b << endl; //a=100return 0;
}
12. const(重要)
12.1 c语言中const修饰的变量为只读变量
#include <stdio.h>int main(int argc, char *argv[])
{//使用const修饰变量,此时该变量为只读变量//只读变量:只能读取该变量的值,但是不能通过该变量对其值进行修改const int a = 10;printf("%d\n",a); //10//a = 1;int *p = &a;*p = 1;printf("%d\n",a); //1return 0;
}
12.2 c++中以常量初始化const变量 产生符号常量表
c++中以常量初始化const变量,不会立即开辟空间,而是 产生符号常量表
,当对其取地址会开辟新的内存地址
。
#include <iostream>using namespace std;void fun01()
{const int num = 10;//此时num在符号常量表中,所以也无法通过变量名修改其值//num = 11;//当对该变量获取地址值,会为其开辟新的内存地址int *p = (int *)#*p = 11;cout << "num =" << num << endl; //num =10cout << "*p =" << *p << endl; //*p =11
}int main(int argc, char *argv[])
{fun01();return 0;
}
12.3 c++中以变量初始化const变量 不产生符号常量表,立即开辟空间
示例:
#include <iostream>using namespace std;void fun02()
{int num = 10;const int a = num;// c++中const修饰变量 如果以变量初始化该变量, 会立即开辟空间不会产生符号常量表int *p = (int *)&a;*p = 11;cout << "a =" << a << endl; //num =11cout << "*p =" << *p << endl; //*p =11
}int main(int argc, char *argv[])
{fun02();return 0;
}
12.4 const修饰的是自定义类变量,立即开辟空间,不产生符号常量表
示例:
#include <iostream>using namespace std;struct Stu
{int age;char name[30];
};void fun03()
{const Stu s1 = {18, "张三"};//s1.age = 19;cout << s1.age << endl; //18Stu *p = (Stu *)&s1;p->age = 20;cout << s1.age << endl; //20
}
int main(int argc, char *argv[])
{fun03();return 0;
}
12.5 gcc 99/g++ 11编译器中const修饰的变量可以作为数组创建时的长度,c不行
不建议使用
const int x = 10;
int nums[x] = {0};
12.6 尽量使用const替代无参宏
- const 有类型,可进行编译器类型安全检查。#define 无类型,不可进行类型检查
- const 有作用域,而#define 不重视作用域。
示例:
#define NUM 97
void fun05()
{int x = NUM;char c = NUM;const int num01 = 10;int a = num01;char c1 = num01; //报错// char *p = num01; //报错
}
13. 引用(重要)
13.1 概述
变量名实质上是一段
连续内存空间
的别名,通过变量来命名一片空间对一段连续的内存空间只能取一个别名吗?int nums[10] = {0}; //nums存储的是一段连续内存空间的首地址
c++中新增了引用的概念,
引用
可以作为一个 已定义的变量的 别名。引用是 c++对 c 的重要扩充,并不是 c++的发明。
13.2 语法
数据类型& 别名(变量名) = 变量;int num = 10;
int& p = num;
13.3 注意
&
在此不是求地址运算,而是起标识作用
。
类型标识符
是指目标变量的类型
必须在
声明引用变量时进行初始化
。引用初始化之后不能改变。
不能有 NULL 引用,必须确保引用是和一块合法的存储单元关联。
int& p = NULL; //报错
可以建立对数组的引用。
13.4 示例1:认识引用
#include <iostream>using namespace std;
void fun01()
{int num = 10;//一个变量可以有 n 个别名int& p = num;p = 20;cout << "num=" << num << endl; //num=20cout << "p=" << p << endl; //p=20num = 30;cout << "num=" << num << endl; //num=30cout << "p=" << p << endl; //p=30
}int main(int argc, char *argv[])
{fun01();return 0;
}
13.5 示例2:使用引用的注意事项
void test02()
{ //1) 引用必须初始化int& ref; //报错:必须初始化引用//2) 引用一旦初始化,不能改变引用int a = 10;int b = 20;int& ref = a;ref = b; //不能改变引用//3) 不能对数组直接建立引用int arr[10];int& ref3[10] = arr;
}
13.6 示例3:建立数组引用
void fun02()
{int nums[10] = {0};//不能对数组直接建立引用//int& ns[10] = nums; //报错//数组建立引用,方式一typedef int arr[10];arr& list = nums;//数组建立引用,方式二int (&ns)[10] = nums;
}
13.7 示例4:建立指针变量的引用
int num = 10;
int *p = #
int *&myP = p;
cout<<"*myP = "<< *myP<< endl;
13.8 示例5:给函数取别名
#include <iostream>using namespace std;void add(int a, int b)
{cout << a << "+" << b << "=" << a+b << endl;
}void fun03()
{void (&myfun)(int a, int b) = add;myfun(12,34);
}int main(int argc, char *argv[])
{fun03();return 0;
}
//12+34=46
13.9 函数中的引用
13.9.1 引入
#include <iostream>using namespace std;
void fun01(int a)
{a = 10;
}void fun02(int *c)
{*c = 100;
}int main(int argc, char *argv[])
{//值传递,在fun01函数里修改值,不会影响实参int b = 1;fun01(b);cout << "b=" << b << endl; //b=1//地址传递,在fun02函数里修改值,会影响实参fun02(&b);cout << "b=" << b << endl; //b=100return 0;
}
13.9.2 引用作为形参
当函数中的 形参是引用时,是地址传递,类似于指针
引用作为函数的参数优点:
1、实参不用取地址
2、引用(形参)不会另辟空间(不占空间)
3、函数内部 不需要指针解引用
示例:引用作为 形参
#include <iostream>using namespace std;void fun03(int& d)
{d = 1000;
}int main(int argc, char *argv[])
{int b = 10;fun03(b);cout << "b=" << b << endl; //b=1000return 0;
}
13.9.2 引用作为返回值
注意:
- 当函数中的
返回值为引用
时,要确保当函数执行完毕后
,引用关联的内存一定要存在
- 可以用static 修饰 要返回的内容;
- 也可以用
动态开辟内存
保证内存不被回收,后面再手动释放。
示例:函数不要返回局部变量的引用
#include <iostream>using namespace std;int& fun04()
{//此时,p指向的是num在栈区的内存地址,函数结束可能已经自动回收//所以在外边可能接收不到//int num = 10000;//static修饰则会改善内存可能自动回收的问题static int num = 10000;int& p = num;return p;
}int main(int argc, char *argv[])
{int& num = fun04();cout << "num=" << num << endl; //num=10000return 0;
}
示例2:链式编程
struct Person
{char name[50];
// void eat(char* foodName)
// {
// cout << name << "吃" << foodName << endl;
// }Person& eat(Person& p, char* foodName){cout << p.name << "吃" << foodName << endl;return p;}
};void fun12()
{Person p = {"张三"};//张三吃油泼面,张三吃甑糕,张三吃棒棒糖
// p.eat("油泼面");
// p.eat("甑糕");
// p.eat("棒棒糖");p.eat(p,"油泼面").eat(p,"甑糕").eat(p,"棒棒糖");
}
13.10 常引用
13.10.1 概念
概念:常量的引用
特点:值不能被修改
13.10.2 示例
void test07(void)
{ const int& num = 10;num = 11;//err 不能被修改
}
13.10.3 常引用作为函数的参数
作用:防止函数内部 通过引用修改外部的值
优点:
引用不产生新的变量
,减少形参与实参传递时的开销。- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
- 如果希望
实参随着形参的改变而改变
,那么使用一般的引用
- 如果
不希望实参随着形参改变
,那么使用常引用
示例:
void fun05(const int& num)
{//num = 1000000; //报错,只读,不能修改cout << num << endl;
}int main(int argc, char *argv[])
{int num = 100;fun05(num);cout << "num=" << num << endl; //num=100return 0;
}
14. 内联函数
14.1 概念
由关键字 inline
修饰的函数 为内联函数。
示例:
inline void add(int a,int b)
{cout << a+b << endl;
}
int main()
{//add(10,2);cout << 10+2 << endl;
}
14.2 特点
内联函数:在编译阶段像宏一样展开。有作用域的限制(作为类的成员函数)。
内联函数为了继承宏函数的效率,没有函数调用时开销,然后又可以像普通函数那样,可以进行参数、返回值类型的安全检查,又可以作为成员函数。
14.3 注意
- inline只能在定义函数的时候修饰。
- 任何在类内部定义的函数自动成为内联函数。
- inline修饰的函数是否为内联函数,
取决于编译器
。对于非inline修饰的函数,也有可能转成内联函数(体积小、功能简单的函数)。
14.4 内联的条件
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
14.5 宏函数和内联函数区别(重要)
宏函数:
- 参数没有类型,不能保证参数的完整型。
- 宏函数 在
预处理阶段
展开。- 宏函数 没有作用域限制 不能作为类的成员。
内联函数:
- 参数有类型 保证参数的完整型。
- 内联函数 在
编译阶段
展开。- 内联函数 有作用域限制 能作为类的成员
15. 形参的默认值
概念:
c++在声明函数原型的时可为一个或者多个参数指定默认(缺省)的参数值,
当函数 调用的时候如果没有指定这个值,编译器会自动用默认值代替。
注意:
- 如果某形参
设置了默认参数
,那么这个形参的后边所有形参都必须设置默认参数
。
示例:
void test10(int a = 1,int b = 10)
{ cout << "a = " << a << endl;cout << "b = " << b << endl;
}
int main(int argc, char *argv[])
{ test10();return 0;
}
16. 占位参数
概念:
只有形参类型 没有形参名的参数 叫占位参数。
示例:
void test11(int a,int)
{ cout << "a = " << a << endl;
}
void test12(int a,int=10)
{ cout << "a = " << a << endl;
}
int main(int argc, char *argv[])
{ test11(1,2);return 0;
}
注意:
- 占位参数、 重载++、 – 运算符才用。
17. 函数重载
函数重载 是静态多态。
函数重载:一个函数名 多个函数功能。
函数重载的条件:同一作用域,函数的参数个数、类型、顺序不同都可以重载,返回值类型不能作为重载条件。
示例:
void test13()
{ cout << "test13-01" << endl;
}
void test13(int i)
{ cout << "test13-02" << endl;
}
void test13(int i,char c)
{ cout << "test13-02" << endl;
}
17.1 底层原理
因为c++在编译函数时会在函数名前随机添加新的名称, 所以此时test13函数将生成以下函数名如:_Z4test13v //v代表无返回值_Z4test13i //i为参数类型的首字母_Z4test13ic //i为第一个参数类型的首字母,c为第二个参数类型的首字母
注意:函数重载和缺省参数 在一起 容易产生二义性
#include <iostream>using namespace std;
void test14(int a)
{cout << "test14-01" << endl;
}
void test14(int a,int b=10)
{cout << "test14-01" << endl;
}int main(int argc, char *argv[])
{//error: call of overloaded 'test14(int)' is ambiguous//调用重载的test14(int)是二义性的test14(12);return 0;
}
18. 混合编程
由于 c++可以使用c的模块中函数,当c++整个工程编译时,可能会将使用c语言编写的函数名编译成c++规则的函数名(定义的函数名前随机添加新的名称),链接程序时,可能会找不到目标函数,因此采用 extern "c"
解决。
c 函数: void MyFunc(){} ,被编译成函数: MyFunc
c++函数: void MyFunc(){},被编译成函数: _Z6Myfuncv
错误演示:
//test.h
#ifndef TEST_H
#define TEST_H
extern void test(int x);
#endif // TEST_H//test.c
void test(int x)
{ } //main.cpp
#include <iostream>
#include "test.h"
using namespace std;
int main(int argc, char *argv[])
{test();return 0;
}
修改:
//test.h
#ifndef TEST_H
#define TEST_H#ifdef __cplusplus
extern "c"{
#endifextern void test(int x);#ifdef __cplusplus
} #endif
#endif // TEST_H