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;
示例:
c
#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 >> ...
示例:
c
#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. 作用域运算符
通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。
符号:
::
作用:作用域运算符可以用来解决局部变量与全局变量的重名问题,即 在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。
示例:
c
#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; } }
注意:
- 命名空间不能定义在函数中,只能定义在全局区域
示例:
c
#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 命名空间只能在全局区域定义(命名空间不能定义在函数中)
示例:
c
#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 命名空间嵌套命名空间
命名空间中可以嵌套命名空间
示例:
c
#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 命名空间的开放性
随时添加新的成员
示例:
c
#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=10
cout << "A::num02=" << A::num02 << endl; //A::num02=10000
return 0;
}
5.2.4 无名命名空间
无名命名空间,意味着命名空间中的标识符 只能在本文件内访问
示例:无名命名空间中的内容 当成员使用,不用加 空间名称::
。
c
#include <iostream>
using namespace std;
namespace {
int num03 = 99999;
}
int main(int argc, char *argv[])
{
cout << "num03=" << num03 << endl; //num03=99999
return 0;
}
5.2.5 取别名
示例:
c
#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=6666
return 0;
}
5.3 using 关键字作用
5.3.1 using声明命名空间中的具体成员
语法:
using 命名空间名 :: 变量名
示例:
c
#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=10
cout << "A::b=" << A::b << endl; //A::b=1
return 0;
}
注意:
- 容易造成同范围内的命名冲突
c
#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=10
cout << "A::b=" << A::b << endl; //A::b=1
return 0;
}
5.3.2 using声明成员函数 遇到函数重载
c
#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 声明整个命名空间
c
#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. 全局变量类型检测增强
c
#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 语言代码:
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中不强转没问题
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++中必须强转,不然会报错
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
c
#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类型中可以有成员函数
c
#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字节。
示例:
c
#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++中,三目运算表达式返回是
变量(地址)
示例:
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=10
cout << "b=" << b << endl; //a=100
return 0;
}
12. const(重要)
12.1 c语言中const修饰的变量为只读变量
c
#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); //1
return 0;
}
12.2 c++中以常量初始化const变量 产生符号常量表
c++中以常量初始化const变量,不会立即开辟空间,而是 产生符号常量表
,当对其取地址会开辟新的内存地址
。
c
#include <iostream>
using namespace std;
void fun01()
{
const int num = 10;
//此时num在符号常量表中,所以也无法通过变量名修改其值
//num = 11;
//当对该变量获取地址值,会为其开辟新的内存地址
int *p = (int *)#
*p = 11;
cout << "num =" << num << endl; //num =10
cout << "*p =" << *p << endl; //*p =11
}
int main(int argc, char *argv[])
{
fun01();
return 0;
}
12.3 c++中以变量初始化const变量 不产生符号常量表,立即开辟空间
示例:
c
#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 =11
cout << "*p =" << *p << endl; //*p =11
}
int main(int argc, char *argv[])
{
fun02();
return 0;
}
12.4 const修饰的是自定义类变量,立即开辟空间,不产生符号常量表
示例:
c
#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; //18
Stu *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不行
不建议使用
c
const int x = 10;
int nums[x] = {0};
12.6 尽量使用const替代无参宏
- const 有类型,可进行编译器类型安全检查。#define 无类型,不可进行类型检查
- const 有作用域,而#define 不重视作用域。
示例:
c
#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:认识引用
c
#include <iostream>
using namespace std;
void fun01()
{
int num = 10;
//一个变量可以有 n 个别名
int& p = num;
p = 20;
cout << "num=" << num << endl; //num=20
cout << "p=" << p << endl; //p=20
num = 30;
cout << "num=" << num << endl; //num=30
cout << "p=" << p << endl; //p=30
}
int main(int argc, char *argv[])
{
fun01();
return 0;
}
13.5 示例2:使用引用的注意事项
c
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:建立数组引用
c
void fun02()
{
int nums[10] = {0};
//不能对数组直接建立引用
//int& ns[10] = nums; //报错
//数组建立引用,方式一
typedef int arr[10];
arr& list = nums;
//数组建立引用,方式二
int (&ns)[10] = nums;
}
13.7 示例4:建立指针变量的引用
c
int num = 10;
int *p = #
int *&myP = p;
cout<<"*myP = "<< *myP<< endl;
13.8 示例5:给函数取别名
c
#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 引入
c
#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=100
return 0;
}
13.9.2 引用作为形参
当函数中的 形参是引用时,是地址传递,类似于指针
引用作为函数的参数优点:
1、实参不用取地址
2、引用(形参)不会另辟空间(不占空间)
3、函数内部 不需要指针解引用
示例:引用作为 形参
c
#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=1000
return 0;
}
13.9.2 引用作为返回值
注意:
- 当函数中的
返回值为引用
时,要确保当函数执行完毕后
,引用关联的内存一定要存在
- 可以用static 修饰 要返回的内容;
- 也可以用
动态开辟内存
保证内存不被回收,后面再手动释放。
示例:函数不要返回局部变量的引用
c++
#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=10000
return 0;
}
示例2:链式编程
c
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 示例
c
void test07(void)
{
const int& num = 10;
num = 11;//err 不能被修改
}
13.10.3 常引用作为函数的参数
作用:防止函数内部 通过引用修改外部的值
优点:
引用不产生新的变量
,减少形参与实参传递时的开销。- 由于引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
- 如果希望
实参随着形参的改变而改变
,那么使用一般的引用
- 如果
不希望实参随着形参改变
,那么使用常引用
示例:
c
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=100
return 0;
}
14. 内联函数
14.1 概念
由关键字 inline
修饰的函数 为内联函数。
示例:
c
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++在声明函数原型的时可为一个或者多个参数指定默认(缺省)的参数值,
当函数 调用的时候如果没有指定这个值,编译器会自动用默认值代替。
注意:
- 如果某形参
设置了默认参数
,那么这个形参的后边所有形参都必须设置默认参数
。
示例:
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. 占位参数
概念:
只有形参类型 没有形参名的参数 叫占位参数。
示例:
c
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. 函数重载
函数重载 是静态多态。
函数重载:一个函数名 多个函数功能。
函数重载的条件:同一作用域,函数的参数个数、类型、顺序不同都可以重载,返回值类型不能作为重载条件。
示例:
c
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为第二个参数类型的首字母
注意:函数重载和缺省参数 在一起 容易产生二义性
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
错误演示:
c
//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;
}
修改:
c
//test.h
#ifndef TEST_H
#define TEST_H
#ifdef __cplusplus
extern "c"{
#endif
extern void test(int x);
#ifdef __cplusplus
}
#endif
#endif // TEST_H