C/C++教程

第三节 day01_C++学习内容

本文主要是介绍第三节 day01_C++学习内容,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

1.全局变量

2.命名空间

3.using声明的使用

4.C++语法增强

5.C++中的const

6.引用

7.引用在函数中的使用

8.引用作为函数的返回值类型使用

9.常值引用

10.指针引用


内容

#include <iostream>
using namespace std;


/****************************************************************************************************
 * 1.全局变量的定义与使用:
 *     ① “::”表示作用域运算符(C++独有,C没有);
 ****************************************************************************************************/
int a = 30;    // 全局变量
void test01()
{
    int a = 40;    // 局部变量
    cout<<"局部变量a="<<a<<endl;    // 就近优先选择局部变量
    cout<<"全局变量a="<<::a<<endl;    // 此时就选择全局变量
}

/****************************************************************************************************
 * 2.命名空间的定义与使用,命名空间内可以使变量,也可以是函数等内容:
 *     ① 只能在全局范围内定义;
 *     ② 命名空间可嵌套;
 *     ③ 命名空间是开放的,可以随时添加新成员进去;
 *     ④ 命名空间中的函数,可以在该命名空间外部定义;
 *     ⑤ 无名命名空间:“namespace {}”,表示该命名空间只能在本文件内访问使用,相当于该其添加了static,使其作为内部
 * 链接,不建议使用该命名空间;
 *     ⑥ 给命名空间取一个别名;
 ****************************************************************************************************/
namespace{    // 无名命名空间
    int b = 25;
    void func()
    {
        cout << "无名命名空间中的func遍历b = " << b << endl;
    }
}
namespace A {    // 定义一个名字为A的命名空间
    int a = 10;
}
namespace B {
    int a = 20;
    int b = 35;
    void func(){    // 定义命名空间中的函数
        cout << "命名空间B中的func遍历a = " << a << endl;    // 这里的a就是B::a,访问同一命名空间内的内容a不用加作用域运算符
    }
}
namespace C {
    int a = 50;
    namespace D {
        int a = 60;
    }
}
namespace A {    // 表示往命名空间A中添加新元素,也可以直接在上面命名空间内添加
    int b = 15;
    void func();    // 在命名空间外编辑成员函数内容,但是需要在命名空间内声明该函数
}
void A::func()    // 在命名空间外定义该编辑空间的成员函数。
{
    cout << "func遍历b = " << b << endl;    // 这里的a就是B::a,自己家的a不需要再加作用域运算符
}
namespace veryLongName {
    int c = 11;
    void func(){
        cout << "func遍历c = " << c << endl;
    }
}

int test02()
{
    func();    // 调用无名命名空间中的函数
    cout << "A::a = :" << A::a << endl;
    cout << "B::a = :" << B::a << endl;
    B::func();    // 调用命名空间B中的函数
    cout << "C::D::a = " << C::D::a << endl;
    A::func();

    // 给命名空间取别名
    namespace vLN = veryLongName;
    vLN::func();
    return 0;
}

/****************************************************************************************************
 * 3.using声明的使用:using编译指令是整个命名空间标识符在该作用空间中可用。
 *     ① 即当我们两个命名空间中都有相同的变量,但是相同变量的值不同,我们如果在某个方法中需要使用某特定的值,我们直接
 * 在该方法开始书写“using namespace A”即可;
 *     ② 缺点是容易冲突,仔细使用;
 *     ④ using namespace A:凡是在本方法中没有定义的变量或方法,在缺省条件下都使用命名空间A中的变量或方法,如果命
 * 名空间中也没有,就从全局查找并使用。
 *     ③ 指明使用命名空间中的某个成员:using B::b;
 ****************************************************************************************************/
void test03()
{
    using namespace B;    // 使用命名空间B
    using B::b;    // 使用命名空间B中特定的元素b
    cout << "命名空间B中的b = " << b << endl;
}

/****************************************************************************************************
 * 4.C++语法增强:
 *     ① C++是强语法语言,所以在函数定义和参数传值的时候必须严格定义函数返回值类型(即函数开头的类型)和传值类型(形
 * 参的类型);
 *     ② C语言中可以不定义形参类型,此时传入的实参可以使任意类型。
 * 5.C++严格的类型转换:不同类型的变量一般是不能直接赋值的,需要相应的强转。
 * 6.C++结构体:
 *     ① 定义结构体时C/C++都需要有关键词“struct”关键词修饰;
 *     ② 在使用结构体时,C++不需要加关键字“struct”,使用修饰也不会报错,而C需要修饰;
 *     ③ 结构体成员既可以是成员变量,也可以是是成员函数,而C只能定义成员变量。
 ****************************************************************************************************/
struct Student{    //定义结构体
    string mName;    // 成员变量
    int mAge;
    void setName(string name){ mName = name; }    // 成员函数
    void setAge(int age){ mAge = age; }    // 严格的类型定义,表现了C++的强语法性
    void showStudent(){ cout << "Name:" << mName << endl << "Age:" << mAge << endl; }
};
void test04()
{
    Student stu;    // 使用结构体时,不需要struct关键词修饰。此句表示引用结构体,并给其取个别名stu
    stu.setName("John");    // 给结构体函数传参,并调用结构体函数
    stu.setAge(20);
    stu.showStudent();
}

/****************************************************************************************************
 * 7.新增“bool”类型关键字:true和false,分别存储为1和0,占一个字节。
 *     ① 其实C语言的C99版本开始有bool类型,但是需要引入包含头文件“stdbool.h”,就可以使用和C++一样的bool类型了。
 * 8.三目运算符:a>b?a:b,在C语言中返回的是数值(即变量的值),而在C++中返回的是变量本身(引用)。
 * 9.C++中的const:
 *     ① 字面意思,常数,不变的数值,它是一个限定符,用来限定一个变量不允许改变,将一个对象转换成一个常量;
 *     ② C++中用const修饰的常量是内部链接常量,即只在本文件中起作用,外部文件不可见(可使用external修饰,用于外部使
 * 用),而C中为外部链接,可外部使用;
 *     ③ C++中const表示常量,不需要分配内存长度,而C中表示只读变量,需要分配内存长度;
 *     ④ C++中全局const,当声明extern或者对变量进行取地址时,编译器会为其分配内存空间,变量存储在只读数据段,否则不
 * 开辟内存空间,内存中的数据不可修改;
 *     ⑤ 当以变量的形式初始化const修饰的变量时,系统也会为其分配内存空间,如“const int a=b;”,系统直接为a开辟空间
 * 而不创建符号表中;
 *     ⑥ 对于自定义数据类型,如类对象,也会分配内存空间,如:const Person person;
 *     ⑦ 尽量用const替换宏(#define)。
 * 10.C中的const:
 *     ① const修饰的全局变量,变量名只读,内存空间在文学常量区(只读),不可修改;
 *     ② const修饰的局部变量,变量名只读,内存分配在栈区(可读写),可通过寻访变量地址来简介修改空间内容;
 ****************************************************************************************************/
void test05()
{
    cout << sizeof (false) << endl;    // 输出结果为1,表示bool类型占一个字节大小
    bool flag = true;    // C语言中没有这种类型
    flag = 100;    // 给bool类型赋值时,非0值会自动转换成true(1),0值直接转换成false(0)
    cout << flag << endl;    // 输出结果为1

    // C++中的const
    const int constA = 10;    // 给常量赋值,变量名和值存于符号常量表,此时还没有开辟内存空间
    cout << "constA:" << constA << endl;    // 输出结果为10,输出符号表中的值
    int* p = (int*)&constA;    // 寻访变量constA的地址,此时给constA开辟内存空间,并将constA=10存储在开辟的内存空间中
    *p = 300;    // 并修改constA地址中的值
    cout << "*p:" << *p << endl;    // 输出结果为300,表示修改成功
    cout << "constA:" << constA << endl;    // 输出结果仍为10,输出的是符号表中的值,但地址中的值已经被修改。
    cout << "*&constA:" << &constA << endl;    // 输出结果为:0x61feb8,存储地址
    cout << "*p:" << *&p << endl;    // 输出结果为:0x61feb8,存储地址

    const Student stu = {"Han", 27};
    cout << "stu.Name: " << stu.mName << endl << "stu.Age: " << stu.mAge << endl;
}

/****************************************************************************************************
 * 11.在C++中尽量使用const替换宏(#define):#define MAX 1024,替换为:const int max=1024
 *     ① const有类型,可进行编译器类型安全检查,使用时调用的是short类型的函数;
 *     ② #define无类型,不可进行类型检查,使用时调用的是int类型重载的函数;
 *     ② const有作用域,可用于顶一个指定作用域下的有效常量,而#define不重视作用域,默认定义处到文件结尾或到
 * #undef A(卸载宏常量)处可用;
 *     ③ 宏可以在命名空间中定义,但是它不属于该命名空间,const属于命名空间中的元素。
 ****************************************************************************************************/
#define PARAM 128
const short param = 128;
void func(short a){
    cout << "short a = " << a << endl;
}
void func(int a){    // 函数的重载,即对上面同一个函数的重新定义,增加其函数功能
    cout << "int a = " << a << endl;
}
void test06()
{
    func(PARAM);    // 输出结果为:int,表示宏调用的是int类型的重载函数
    func(param);    // 输出结果为:short,表示宏调用的是short类型的重载函数
}

/****************************************************************************************************
 * 12.引用(reference),C++中能用引用绝不用指针,它是C++给函数传递地址的途径,可以简单理解为给已有的变量取一个别名。
 *     ① &别名:表示引用,&在此不是求地址运算,而是起标识作用;
 *     ② 给某个变量取别名,就定义某个变量,即操作别名就相当于操作原;
 *     ③ 从上到下替换,之后操作别名就相当于操作变量本身;
 *     ④ 一个变量可以有多个引用,引用一旦初始化之后,就不能修改。
 *     ⑤ 不能给“数组”取别名,但是有方法给数组取别名:
 *         i> 用小括号提高优先级取别名;
 *         ii> 用typedef给数组类型取个别名;
 *     ⑥ 其实引用的本质是常量指针:Type& ref = val; // Type* const ref = &val;内部实现用户不可见
 ****************************************************************************************************/
void test07(){
    int a = 10;
    int& b = a;    // 给变量 a 取一个别名 b
    // int *b = &a;    // 这是取地址,上面是取别名,即引用变量
    cout << "a:" << a << endl;    // 10
    cout << "b:" << b << endl;    // 10
    cout << "------------" << endl;
    //操作 b 就相当于操作 a 本身
    b = 100;
    cout << "a:" << a << endl;    // 100
    cout << "b:" << b << endl;    // 100
    cout << "------------" << endl;
    //一个变量可以有 n 个别名
    int& c = a;
    c = 200;
    cout << "a:" << a << endl;    // 200
    cout << "b:" << b << endl;    // 200
    cout << "c:" << c << endl;    // 200
    cout << "------------" << endl;
    //下面是取地址符,a,b,c的地址都是相同的,而变量之间的赋值,其地址是不同的。
    cout << "a:" << &a << endl;    // 0x61feb4
    cout << "b:" << &b << endl;    // 0x61feb4
    cout << "c:" << &c << endl << endl;    // 0x61feb4

    /*** 给数组取别名 ***/
    int veryLongNameArray[5] = {11, 22, 33, 44, 55};

    // 方法i> 用小括号提高优先级取别名;
    int (&sArr)[5] = veryLongNameArray;    // 因为&引用标识符的优先级<中括号,所以要加个小括号

    // 方法ii> 用typedef给数组类型取个别名;
    typedef int TYPE_ARR[5];    // 先定义一个数组类型,(TYPE_ARR就是一张数组类型,其中有5个int类型的元素)
    TYPE_ARR &new_arr = veryLongNameArray;    // 再用该数据类型继承或者赋值veryLongNameArray的值

    for(int i=0;i<5;i++){
        cout << sArr[i] << " ";
        cout << new_arr[i] << "| ";
    }
    cout << endl;
}

/****************************************************************************************************
 * 13.引用(reference)在函数中的使用,引用作为函数的参数使用:
 *     ① 函数内部修改函数外部的值要么使用地址,要么使用引用(推荐);
 *     ② 通过引用参数产生的效果同按地址传递是一样的。
 *     ③ 引用的语法更清楚简单:
 *         1) 函数调用时传递的实参不必加“&”符
 *         2) 在被调用函数中不必在参数前加“*”符。
 *     ④ 引用作为其它变量的别名而存在,因此在一些场合可以代替指针。
 ****************************************************************************************************/
void mySwap1(int a, int b)    // 一个简单的交换函数,但是这种交换只在该方法内起作用,对外不起作用
{
    int temp = a;
    a = b;
    b = temp;
}
void mySwap2(int *a, int *b)    // a=&data,b=&data2,因为交换的是地址里的内容,所以对外起作用
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
void mySwap3(int &a, int &b)    // 给参数取别名
{
    int temp = a;
    a = b;
    b = temp;
}
void test08()
{
    int data1 = 10, data2 = 20;
    cout << "原始数据:data1 = " << data1 << ", data2 = " << data2 << endl;    // 原始数据
//    mySwap1(data1, data2);
//    cout << "第一种交换:data1 = " << data1 << ", data2 = " << data2 << endl;    // 第一种值传递,交换失败
//    mySwap2(&data1, &data2);
//    cout << "第二种交换:data1 = " << data1 << ", data2 = " << data2 << endl;    // 第二种地址传递:交换成功
    mySwap3(data1, data2);
    cout << "第三种交换:data1 = " << data1 << ", data2 = " << data2 << endl;    // 第三种引用传递:交换成功
}

/****************************************************************************************************
 * 14.引用作为函数的返回值类型使用:
 *     ① 不能返回局部变量的引用,因为局部变量在函数结束时,内存内容将被释放,引用无效,所以返回空,但是当不使用引用时
 * 局部变量的值可以被返回;
 *     ② 可以返回静态变量的引用,因为静态变量的生命周期较长,所以引用有效。但是静态变量只会作用1次,所以第二次调用静
 * 态变量时,就不起作用了;
 *     ③ 函数返回,优先返回该变量的值,但是当函数作为左值被赋值时,返回的是引用(当然,如果函数返回值类型不是引用,那
 * 么函数定义不合法,报错)而不是值;所以作为右值,返回的自然就是值。
 ****************************************************************************************************/
//返回局部变量引用
int& func01(){
    int a = 10; //局部变量
    return a;
}
//返回静态变量引用
int& func02(){
    static int a = 20;
    cout << "static int a : " << a << endl;
    return a;
}
void test09()
{
//    //不能返回局部变量的引用
//    int& ret01 = func01();    // 现在ret01是函数返回值a的别名
//    cout << "func01: ret01 = " << ret01 << endl;    // 函数结束局部变量被释放,所以返回空

    cout << func02() << endl;    // 打印输出20,函数内的cout,和该行的cout输出结果一样
    // 如果函数做左值,那么必须返回引用,如下
    func02() = 100;    //打印输出20,为函数内的cout输出;此句返回的是a,即此句就相当于a=100
    cout << func02() << endl;     // 打印输出100 100,因为第二次调用静态变量的定义不起作用,所以函数内打印输出为100,此句打印输出也是100
}

/****************************************************************************************************
 * 15.常左值引用,即给左值取一个别名:左值一般为变量名a=2,a即为左值,2即为右值。
 *     ① 定义格式:const Type& ref = val;
 *     ② 字面量不能赋给引用,但是可以赋给 const 引用 const 修饰的引用,不能修改;
 *     ③ 将函数的形参定义为常量引用的好处: 引用不产生新的变量,减少形参与实参传递时的开销。
 *     ④ 由于引用可能导致实参随形参改变而改变,将其定义为常量引可以消除这种副作用。如果希望实参随着形参的改变而改变,
 * 那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
 * 16.常右值引用,即给右值取一个别名:好处是该引用将不能被修改。
 ****************************************************************************************************/
typedef struct {
    int num;
    char name[16];
} STU;
void printSTU01(STU tmp)    // 普通结构体变量作为形参,开销太大,使用引用减小开销
{
    cout << "姓名: " << tmp.name << ", 年龄: " << tmp.num <<"; 占用空间大小:"<< sizeof (tmp) << endl;
    // 姓名: Lucy, 年龄: 16; 占用空间大小:20,其中20字节=name16字节+num4字节,此时形参tmp开辟了存储空间。

}
void printSTU02(STU &tmp)    // 使用引用减小开销,此时传入的参数是变量lucy,即“STU &tmp = lucy”,此时tmp只是lucy的别名
{
    tmp.num = 80;    // 内容可被修改
    cout << "姓名: " << tmp.name << ", 年龄: " << tmp.num <<"; 占用空间大小:"<< sizeof (tmp) << endl;
    // 姓名: Lucy, 年龄: 80; 占用空间大小:20,其中20字节=name16字节+num4字节,但是此时别名tmp并没有开辟存储空间。
}
void printSTU03(const STU &tmp)    // 常引用,使其传入内容不可修改
{
    // tmp.num = 80;    // 此句报错,说明不可被修改
    cout << "姓名: " << tmp.name << ", 年龄: " << tmp.num <<"; 占用空间大小:"<< sizeof (tmp) << endl;
    // 姓名: Lucy, 年龄: 16; 占用空间大小:20,其中20字节=name16字节+num4字节,但是此时别名tmp并没有开辟存储空间。
}
void test10()
{
    STU lucy = {16, "Lucy"};
    printSTU01(lucy);
    printSTU02(lucy);
    printSTU03(lucy);

    const int &num = 10;    // 按照C++的语法,此处的10的类型为const int,而非int,所以此句签名使用的是const int
    cout << "num = " << num << endl;
}

/****************************************************************************************************
 * 17.指针引用:
 *     ① 好处是传参时,不需要取地址,方便操作;
 ****************************************************************************************************/
#include<stdlib.h>
#include<string.h>
// 在C中使用指针的的实现方法
void myStr1(char **p_str)    // 形参p_str == &str,*p_str == *&str ==str
{
    *p_str = (char *)calloc(1, 32);    // 申请空间用于写入内容
    strcpy(*p_str, "Hello World!");    // 写入内容
}
//在C++中使用引用实现该方法,指针的引用
void myStr2(char* &r_str)    // char* &r_str == str,即r_str == str
{
    r_str = (char *)calloc(1, 32);
    strcpy(r_str, "Hello World!");
}
void test11()
{
    // 需求:封装一个函数,从堆区给str申请一个空间并赋值为“Hello World!”
    char *str = NULL;
//    myStr1(&str);    // 在C中使用指针就传入地址,并往其地址内写入内容
    myStr2(str);    // 在C++中使用引用就直接传入变量名即可,
    cout << "str = " << str << endl;
    free(str);    // 释放堆区
}


int main()
{
//    test01();
//    test02();
//    test03();
//    test04();
//    test05();
//    test06();
//    test07();
//    test08();
//    test09();
//    test10();
//    test11();
    return 0;
}
这篇关于第三节 day01_C++学习内容的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!