C++第一天

139 阅读1分钟

冒号作用域运算符

int atk = 200;
int main() {
    int atk = 100;
    cout << "atk = " << atk << endl;  // 输出100
    // :: 前面啥也没有,就是全局作用域
    cout << "atk = " << ::atk << endl;  // 输出200
    return 0;
}

namespace的使用

作用

  • 解决名称冲突问题
  • 必须在全局作用域下声明
  • 命名空间中可以放函数,变量,类,结构体。。。
  • 命名空间可以套用另一个命名空间
  • 命名空间是开放的,可以随时往原来的命名空间内放新内容
  • 匿名命名空间
  • 可以起别名
void test1() {
    LOL::GoAtk();
    WZ::GoAtk();
}
// 放各种数据
namespace A {
    int a = 10;
    void func();
    struct Person {

    };
    class Animal{};
    namespace B { // 命名空间的嵌套
        int m_A = 20;
    }
}
void test02() {
    cout << "作用域B下的m_A为:" << A::B::m_A << endl;
}
// 会和原来的命名空间合并,并且将内容追加
namespace A {
    int m_B = 20;
}
void test03() {
    cout << A::m_B << endl;
}

namespace veryLongName {
    int m_A = 10;
}
void test04() {
    // 起别名
    namespace veryShortName = veryLongName;
    cout << veryLongName::m_A << endl;
    cout << veryShortName::m_A << endl;
}
// 匿名命名空间
// 相当于 static int m_C, static int m_D
namespace {
    int m_C = 0;
    int m_D = 0;
}
int main() {
    // test1();
    //test02();
    //test03();
    test04();
    // namespace 用来解决命名冲突的问题
    return 0;
}
// 解决名称冲突
// game1.h
namespace LOL {
    void Tak();
}
// game1.cpp
void LOL::Tak() {
    cout << "LOL" << endl;
}


// game2.h
namespace WZ {
    void Tak();
}
// game2.cpp
void WZ::Tak() {
    cout << "WZ" << endl;
}
int main() {
    cout << LOL::Tak() << endl;
    cout << WZ::Tak() << endl;
}

using声明和using编译指令

namespace KingGlory {
    int sunwukongID = 10;
}

void test01() {
    int sunwukongID = 20;
    using KingGlory::sunwukongID;  
    // using声明,注意避免二义性问题
    // 上面的using声明表示要使用KingGlory下的sunwukongID
    // 但是就近原则
    cout << sunwukongID << endl;  // 会报错,多次声明
    // cout <<KingGlory::sunwukongID << endl;
    // 即使这样也不行
}
namespace LOL {
    int sunwukongID = 30;
}
void test02() {
    int sunwukongID = 40;
    using namespace KingGlory; // 打开KingGlory房间,不一定用
    using namespace LOL;  // 打开LOL房间,不一定用
    cout << sunwukongID << endl;
}
int main() {
    test01();
    return 0;
}

C++对C语言的增强(对比着看)

  • 全局变量的增强
// c语言中,可以编译通过
int a;
int a = 10;
int main() {
    printf("%d\n", a);
    return 0;
}
// c++语言中,不能编译通过
int a;
int a = 10;  // 重定义
int main() {
    printf("%d\n", a);
    return 0;
}
  • 函数检测增强
// c语言
int GetMax(a, b) {
// 只有一个警告,要有返回值
}
void test02() {
    GetMax(10, 10, 10);  // 啥都没报
}
int main() {
    //printf("%d\n", a);
    test02();
    return 0;
}
// c++
// 函数检测增强,参数类型检测增强,返回值检测增强, 函数调用参数检测增强
int GetMax(int a, int b) {
	return (a > b ? a : b);
}
void test02() {
    GetMax(10, 10);
}
int main() {
    //printf("%d\n", a);
    test02();
    return 0;
}

类型转换检测增强

// c语言
void test03(){
    // 可以编译通过
    char* p = malloc(sizeof(64));  // malloc返回值是void*;
}
void test03(){
    // 可以编译通过
    char* p = (char*)malloc(sizeof(64));  // 必须进行类型转换;
}

struct增强

// C++
struct Person {
    int age;
    void plusAge(){
        age++
    };  // C语言中,结构体内不能声明函数,C++可以
}
void test04() {
    Person p1;
    p1.age = 100;
    p1.plusAge();
    cout << p1.age << endl;
}
int main() {
    test04();
    return 0;
}

三目运算符增强

void test05() {
    int a = 10;
    int b = 20;

    cout << "ret = " << (a > b ? a : b) << endl;

    (a < b ? a : b) = 100;  // C++中返回的是变量

    cout << "a = " << a << endl;  // 结果是100
    cout << "b = " << b << endl;
}
int main() {
    test05();
    return 0;
}

const增强

const int a = 10;
int* pa = (int*)&a;
*pa = 100;
printf("%d\n%d\n", *pa, a);
// 结果是100,100

// 但是在C++中就不一样了
const int a = 10;
int* pa = (int*)&a;
*pa = 100;
cout << "*pa = " << *pa << endl;  // 结果是100
cout << " a = " << a <<end;  // 结果是10
// a的值并没有改变

在C++中,const int a = 10

创建了一张键值表,并不会实际开辟内存空间

当执行int* pa = (int*)&a时,操作系统会给pa开辟一块新的空间,空间内存放的值是10,当执行*pa时,操作的是新的空间,而不是对a进行修改

int* pa = (int*)&a;
// 相当于
int tmp = a;  // 创建了一个临时空间
int* pa = (int*)&tmp;
// pa操作的是tmp那块空间

所以,如果使用const声明某个变量,并且为这个变量分配内存的话,我们就可以通过指针修改绕过const的限制来修改变量了

int a = 10;
const int b = a;  // 分配了内存
int* pb = (int*)&b;
cout << "*pb = " << *pb << endl;
cout << "b = " << b << endl;  // 结果都是10

自定义类型也可以分配内存,所以也可以绕过const限制来修改

struct Person {
    int age;
    string name;
};
int main() {
    const Person p1 = { 18, "sb" };
    Person* p = (Person*)&p1;
    p->age = 10;
    p->name = "nb";
    cout << p1.age << endl; // 也可以被修改
    cout << p1.name << endl;  // 可以修改
    return 0;
}

内部和外部链接

C语言中,const默认是外部链接,如果一个工程内的两个文件都包含const int a

则会报错,如果要使用,要加上extern

但是在C++中,const默认是内部链接,如果一个工程内的两个文件都包含const int a

就不会报错,如果要使用的话,就要在这两个const声明的变量前都加上extern,用来提升作用域

引用

基本用法

type& 新名字 = 原名

void test01() {
    int a = 10;
    int& b = a;
    b = 100;
    cout << "a = " << a << endl;  // 结果是100
    cout << "b = " << b << endl;  // 结果是100
    cout << &a << endl;
    cout << &b << endl;
    // 地址相同
}

引用必须进行初始化

int& ref;
// 报错

引用初始化后的指向不可以修改

void test02() {
    int a = 10;
    int b = 20;
    int& ref1 = a;
    int& ref2 = b;
    ref1 = ref2;
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "ref1 = " << ref1 << endl;
    cout << "ref2 = " << ref2 << endl;
    cout << "a的地址:" << &a << endl;
    cout << "ref1的地址:" << &ref1 << endl;
    cout << "b的地址:" << &b << endl;
    cout << "ref2的地址:" << &ref2 << endl;
}

ref1初始化的时候指向的是a,

ref2初始化的时候指向的是b,

后面,ref1 = ref2的时候,并没有改变指向,

通过打印地址,可以看到,指向没有改变

对数组的引用

void test03() {
    // 数组的引用
    // 方式一
    typedef int Arry[10];  // 将含有10个整型元素的数组起了一个别名Arry
    int arr[10] = { 0 };
    Arry& aref = arr;
    // int& aref[10] = arr;  // 不能直接建立引用
    for (int i = 0; i < 10; i++) {
        aref[i] = i;
    }
    for (int i = 0; i < 10; i++) {
        cout << aref[i] << " ";
    }
    cout << endl;
    
    
    // 方式二
    int(&ref2)[10] = arr;
    for (int i = 0; i < 10; i++) {
        ref2[i] = i;
    }
    for (int i = 0; i < 10; i++) {
        cout << ref2[i] << " ";
    }
}

函数中的引用

引用最常见的地方是函数的参数和返回值

函数参数

void MySwap(int& m, int& n) {
    int tmp = m;
    m = n;
    n = tmp;
}
void test04() {
    int a = 10;
    int b = 20;
    MySwap(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b <<endl;
}

可见引用是比较方便的,形参不用加'*',实参不用加'&'

引用的本质

本质就是一个指针常量

int a = 10;
int& ref = a;
// 相当于const int* p = &a
// 这也就是为什么一定要进行初始化,并且初始化必须要合法的内存

指针的引用

在C语言中,如果要修改指针的指向,通常可以使用二级指针来修改

但是在C++中就可以通过引用来实现该功能,更简单,明了

struct Person {
    int age;
};
void AllocByPtr(Person** pp) {
    *pp = (Person*)malloc(sizeof(struct Person));
    (*pp)->age = 18;
}
void AllocByRef(Person* &pp) {
    pp = (Person*)malloc(sizeof(Person));
    pp->age = 10000;
}

void test01() {
    Person* p = NULL;
    AllocByPtr(&p);
    cout << "p->age = " << p->age << endl;
}

void test02() {
    Person* p = NULL;
    AllocByRef(p);
    cout << "p->age = " << p->age << endl;
}
int main() {
    // test01();
    test02();
    return 0;
}

常量的引用

void test01() {
    const int& ref = 10;   // 相当于 int tmp = 10; int& ref = tmp;
    // 分配了内存空间的都可以通过指针来修改
    int* pref = (int*)&ref;
    *pref = 1000;
    cout << "ref = " << ref << endl;
}

// 常量引用的使用场景,用来修饰形参
void ShowValue( const int& ref) {
    // ref += 100;   // 如果只想展示之而不改动的话,在传递形参的时候就要加上const
    cout << "ref = " << ref << endl;
}
void test02() {
    int a = 999;
    ShowValue(a);
}
int main() {
    //test01();
    test02();
    return 0;
}