02-C++ 与C的差异

c++ 与c的差异

1. QT中文乱码问题

工具 -- 选项 -- 行为 -- 文件编码改为system

02-C++ 与C的差异_第1张图片

注意:

  • 修改后新项目中文才不会乱码,如果是原有项目需重建 。

2. 输出

语法:

cout << 输出内容1 << 输出内容2 << ... << endl;

注意:

  • 输出的内容中 endl 表示为换行

  • c语言的输出是 stdio.h 中提供了 printf 函数进行标准输出

    • printf("输出的格式",值1,值2,值3,...);
      
  • c++的输出是 iostream 中提供的 cout 变量进行标准输出

    • cout << 值1 << 值2 << 值3...endl;
      

示例:

#include 

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 

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

02-C++ 与C的差异_第2张图片
02-C++ 与C的差异_第3张图片

4. 作用域运算符

通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量。

符号:

::

作用:作用域运算符可以用来解决局部变量与全局变量的重名问题,即 在局部变量的作用域内,可用::对被屏蔽的同名的全局变量进行访问。

示例:

#include 

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 

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 

using namespace std;
int main(int argc, char *argv[])
{ 
    namespace A {
		int a = 10;
	} 
    cout << A::a << endl;
	return 0;
} 
//此时报错
5.2.2 命名空间嵌套命名空间

命名空间中可以嵌套命名空间

示例:

#include 

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 

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 无名命名空间

无名命名空间,意味着命名空间中的标识符 只能在本文件内访问

示例:无名命名空间中的内容 当成员使用,不用加 空间名称::

#include 

using namespace std;

namespace {
    int num03 = 99999;
}

int main(int argc, char *argv[])
{
    cout << "num03=" << num03 << endl;    //num03=99999
    return 0;
}
5.2.5 取别名

示例:

#include 

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 命名空间名 :: 变量名

示例:

#include 

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;
}

注意:

  • 容易造成同范围内的命名冲突
#include 

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声明成员函数 遇到函数重载
#include 

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 

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 

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 
//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 

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 
#include 
#include 

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 
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 

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 

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 

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修饰的变量为只读变量

#include 

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变量,不会立即开辟空间,而是 产生符号常量表,当对其取地址会开辟新的内存地址

#include 

using namespace std;

void fun01()
{
    const int num = 10;
    //此时num在符号常量表中,所以也无法通过变量名修改其值
    //num = 11;
    //当对该变量获取地址值,会为其开辟新的内存地址
    int *p = (int *)&num;
    *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变量 不产生符号常量表,立即开辟空间

示例:

#include 

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修饰的是自定义类变量,立即开辟空间,不产生符号常量表

示例:

#include 

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不行

不建议使用

const int x = 10;
int nums[x] = {0};

12.6 尽量使用const替代无参宏

  1. const 有类型,可进行编译器类型安全检查。#define 无类型,不可进行类型检查
  2. 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 注意

  1. & 在此不是求地址运算,而是 起标识作用

  2. 类型标识符 是指 目标变量的类型

  3. 必须在 声明引用变量时进行初始化

  4. 引用初始化之后不能改变。

  5. 不能有 NULL 引用,必须确保引用是和一块合法的存储单元关联。

    int& p = NULL; //报错
    
  6. 可以建立对数组的引用。

13.4 示例1:认识引用

#include 

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:使用引用的注意事项

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 = &num;
int *&myP = p;
cout<<"*myP = "<< *myP<< endl;

13.8 示例5:给函数取别名

#include 

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 

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、函数内部 不需要指针解引用

示例:引用作为 形参

#include 

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 修饰 要返回的内容;
    • 也可以用 动态开辟内存 保证内存不被回收,后面再手动释放。

示例:函数不要返回局部变量的引用

#include 

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:链式编程

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=100
    return 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 

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 
#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"{
#endif
    
extern void test(int x);
    
#ifdef __cplusplus
} 

#endif
#endif // TEST_H

你可能感兴趣的:(C/C++,c++,c语言)