C++学习总结

本文详细介绍了C++中的字面量、宏替换、枚举类型、初始化方法(构造式、赋值式、列表式),以及复制构造、赋值行为、深浅拷贝的区别和重载。作者还探讨了如何通过关键字如`explicit`、`delete`来控制构造函数和赋值操作的行为。
摘要由CSDN通过智能技术生成

1. 字面量

1.1 宏替换——与类型别名对比
 #define Monday 1
自定义字面量
 // 有错!!!!
 int operator""_$ (int a) {
     return a;
 }
 ​
 printf("%d", 5_$);

2. 类型

2.1 枚举
  • 非匿名枚举类:enum Week{Monday=1, Tuesday}

    • 作用域:整个空间内,而非“enum Week”中

  • 匿名枚举类:enum {Monday, Tuesday}

    • 作用域同上

  • 强类型枚举:enum class Week {Monday, Tuesday}

    • 能够进行作用域隔离,引用时Week::Monday

3. 类型基础行为

3.1 初始化
3.1.1 构造式初始化
 struct A {
     A() {
         a = 0;
     }
     A(int a) {
         this->a = a;
     }
     
     int a;
 }
 ​
 // 栈对象
 A a;
 A b(1);
 ​
 // 堆对象
 A *a = new A;
 A *b = new A();
 A *c = new A(1);

3.1.2 赋值式初始化
 struct A {
     A(int a) {
         this->a = a;
     }
     
     int a;
 };
 ​
 int main() {
     A a = 1;
 }

问题:

  1. 看起来容易误解成赋值

  2. 不支持多个入参的构造

  3. 不支持堆对象的双重初始化

 // 赋值式:error!
 *(int* ptr = new int) = 5;
 ​
 // 构造式:
 int *ptr = new int(5);

如果想让构造函数仅支持构造式,不支持赋值式:关键字explicit

 struct A {
     explicit A(int a) {
         this->a = a;
     }
     
     int a;
 };
 ​
 int main() {
     A a(1);
     // error!
     // A a = 1;
 }

3.1.3 列表式初始化
 struct A {
     A(int a, int b) {
         this->a = a;
         this->b = b;
     }
     int a, b;
 };
 ​
 int main() {
     A a {1, 1};
     
     int *pa {new int {1}};
 }

3.2 自动类型识别

关键字:auto

 auto i = 120;   // int

3.3 初始化类成员
  • 构造函数中赋值,是为成员数据“赋值”,而非“初始化”

  • 成员初始化列表

    • 位于构造函数函数名与函数体之间,并非“赋值”;

    • 可以为构造式或列表式,不能是赋值式

    • 初始化顺序:成员变量声明顺序,而非初始化列表中顺序

 struct A {
     A(int a, int b)
         : a {a}, b(b)
     {}
     int a, b;
 };
  • 成员声明式初始化:

    • 可以为赋值式或列表式,不能是构造式

    • C++ 98不允许;C++ 11允许

 struct A {
     int a = 1;
     int b {3};
 };

3.4 复制构造行为
  • 浅拷贝:仅复制目标对象的对应内存,即引用本身的地址

  • 深拷贝:拷贝引用的目标对象的全部内容

  • C++默认的拷贝行为是将来源对象的内存内容拷贝

    • 如对于栈中变量:int i;A a;等,进行A b = a;后,b复制了a的全部内容,即深拷贝

    • 如对于堆中变量:A *a = new A();,由于a是指针,b复制的仅是a的指针内容,即浅拷贝

3.4.1 定制拷贝代码
 struct A {
     A(int a)
         : a(1)
     {}
     A(A const& other)
         : a(other.a)    
     {}
     
     int a;
 };

3.4.2 堆变量的深拷贝

下类中声明了一个指针,并在堆中为其开空间

 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}    
     
     ~A() {
         delete pa;
     }
         
     int* pa;
 };
 ​
 // test
 A a(1);
 A b(a);
 ​
 cout << a.pa << endl;
 cout << b.pa << endl;

在进行test后会发现,b变量的pa地址和a的地址一样,也就是进行了浅拷贝;虽然程序会无法正常推出,这可能是因为A并没有实现A(A const& a){}构造函数。

接下来为A实现深拷贝构造函数

 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}
     A(A const& other)
         : pa(new int(*(other.pa)))
     {}
     
     ~A() {
         delete pa;
     }
         
     int* pa;
 };
 ​
 // test
 A a(1);
 A b(a);
 // 或:A b = a;
 ​
 cout << a.pa << endl;
 cout << b.pa << endl;

在Clang中,新实现的拷贝函数被称为“Copy constructor”,同时,Clang-Tidy希望“Clang-Tidy: Copy constructor should not be declared explicit”。

3.4.3 禁用拷贝构造
 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}
     A(A const&) = delete;
     
     ~A() {
         delete pa;
     }
         
     int* pa;
 };

3.5 赋值行为
3.5.1 初始化与赋值——拷贝构造函数和赋值函数
 A a;
 ​
 // 1
 A b = a;
 ​
 //2
 A b;
 b = a;

在1中,是对b通过“拷贝构造函数”进行初始化;在2中,第一行完成初始化,第二行利用“赋值函数”进行赋值。

因此上节禁用拷贝构造函数,但仍可以进行赋值(但是进行堆变量的浅拷贝)。

3.5.2 重载“operator=”实现赋值函数深拷贝
 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}
     A(A const& other)
         : pa(new int(*(other.pa)))
     {}
 ​
     A& operator= (A const& other) {
         // 1
         if (this == &other) return *this;
 ​
         if (other.pa != this->pa) {
             delete this->pa;
             pa = new int(*other.pa);
             return *this;
         }
     }
 ​
     ~A() {
         delete pa;
     }
 ​
     int* pa;
 };
 ​
 ​
 A a(1);
 A b(2);
 b.operator=(a);
 // 更为常用的:b = a;
 ​
 cout << *a.pa << endl;
 cout << *b.pa << endl;

1部分是为了处理自我赋值;

之所以赋值函数返回类型是A&,是为了支持语法a=b=c

3.5.3 禁止赋值
 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}
     A(A const&) = delete;
 ​
     A& operator= (A const&) = delete
 ​
     ~A() {
         delete pa;
     }
 ​
     int* pa;
 };

3.5.4 拷贝构造函数和赋值函数的关系
 struct A {
     explicit A(int a)
         : pa(new int(a))
     {}
     A(A const& other)
         : pa(new int(*(other.pa)))
     {
         cout << "constructor" << endl;
     }
 ​
     ~A() {
         delete pa;
     }
 ​
     int* pa;
 };

如果没有使用“explicit”修饰拷贝构造函数(使用是不被建议的),那么可能会用拷贝构造函数来进行赋值操作(至少我的编译器不会),一些解决方式:

  • 禁用拷贝构造函数

  • 将拷贝构造函数使用“explicit”修饰

  • 明确重载赋值函数“operator=”

  • 将赋值函数“operator=”标为delete

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值