C++类和对象(中)

简介: 祖师爷在设计 C++ 中的类时,规定每个类中都有六个默认成员函数,即使我们不主动写,编译器也会自动生成,这些成员函数就是神秘的天选之子,不仅神秘,而且还很强大,可以这么说:类和对象的是否学懂,取决于对这几个天选之子的理解程度。本文将会逐一介绍这几个默认成员函数,跟随我的脚步,一起揭开他们的神秘面纱

✨个人主页: Yohifo

🎉所属专栏: C++修行之路

🎊每篇一句: 图片来源


I do not believe in taking the right decision. I take a decision and make it right.


我不相信什么正确的决定。我都是先做决定,然后把事情做好。


a4c6cd69eb0ac6bf8e571cd4fd2ca3c.png


📘前言


祖师爷在设计 C++ 中的类时,规定每个类中都有六个默认成员函数,即使我们不主动写,编译器也会自动生成,这些成员函数就是神秘的天选之子,不仅神秘,而且还很强大,可以这么说:类和对象的是否学懂,取决于对这几个天选之子的理解程度。本文将会逐一介绍这几个默认成员函数,跟随我的脚步,一起揭开他们的神秘面纱


a1487e1f4d8a52f174b2db27df01ef1.png


注意:以上函数都需要加上 “默认” 前缀,因为编译器自动生成并调用的是默认成员函数


📘正文

📖默认成员函数

祖师爷规定,每个类中都必须有这六个默认成员函数:


默认构造函数 重要

默认析构函数 重要

默认拷贝构造函数 较重要

默认赋值重载函数 较重要

默认取地址重载函数 一般

默认const取地址重载函数 一般

虽说都是祖师爷钦定的天选之子,但最后两个相对来说比较简单,因此介绍也会比较少


默认成员函数规则比较多,尤其是构造和析构,当初祖师爷在设计的时候,部分地方设计欠佳,导致后人在学习 C++ 时,额外增加了不少学习成本

2576e4e327303a12152bb2232a47c6b.png


下面就来看看祖师爷是如何设计的、出了什么问题、以及是怎么解决的


📖构造函数


构造函数是祖师爷首先钦定的天选之子


构造函数诞生的目的是为了减少频繁手动初始化的问题,将初始化这个事情变成自动化处理

8c9ed317f05e06549f12b09b4da3c12.png


将C语言和C++都看作车辆,初始化操作看作换挡,可以这样认为:


C语言依赖于手动操作,就像手动挡车辆,有驾驶乐趣,但比较麻烦

C++面向对象自动操作,就像自动挡车辆,上手简单,驾驶难度低

a032cc1f193b733580b42d7a7ea0d19.png

我们是可以自己编写构造函数的,祖师爷给了我们这个权力,他钦定的天选之子是默认构造函数,由编译器自动生成,并供类默认调用的,下面来看看编写构造函数的规则


本文介绍的函数都属于特殊函数,规则和普通函数不同


构造函数创建规则:


函数名和类名相同

不需要返回值,甚至连 void 都不需要写

对象实例化时,编译器自动调用默认构造函数

构造函数支持重载,即可以存在多个构造函数,但默认构造函数只能有一个

构造函数还有一种特殊形式:默认构造函数


语法规定,不带参数或参数为全缺省的构造函数称为默认构造函数


默认构造函数有两种写法,推荐全缺省参数的形式


class Date
{
public:
  //特别注意:默认构造函数只允许存在一个形式
  //一般推荐使用形式二:全缺省参数
  //因为这样方便后续初始化时指定值
  默认构造函数形式一:不带参数
  //Date()
  //{
  //  _year = 1970;
  //  _month = 1;
  //  _day = 1;
  //}
  //默认构造函数形式二:参数为全缺省
  Date(int year = 1970, int month = 1, int day = 1)
  {
  _year = year;
  _month = month;
  _day = day;
  cout << "Date(int year = 1970, int month = 1, int day = 1)" << endl;
  }
  //其他普通构造函数,只要与默认构造函数构成重载,都合法
  Date(double b)
  {
  _year = 2023;
  _month = 2;
  _day = 9;
  cout << "Date(double b)" << endl;
  }
  //……
private:
  int _year;
  int _month;
  int _day;
};

严格来说,现阶段的初始化方式不规范,此时为赋值


正确的初始化方式是使用初始化列表,当然这个东西在下篇介绍


7d1f8f7c5e09f27e7115e7b3f712db0.png

出自 《Effective C++》


当构造函数写好后,我们就可以这样使用:


int main()
{
  //调用默认构造函数,d1 初始化为1970 1 1
  Date d1;  //相当于 Date d1(); 但不能这样写
  //因为调用的是全缺省参数的默认构造函数,我们也可以传参数
  //Date d1(2001, 1, 1);  //这种初始化也是合法的
  //调用自定义构造函数
  Date d2(1.1);
  return 0;
}


构造函数允许重载,因此调用不会冲突,运行结果如下:


43fd203af3ab95b672cea4eea3781b5.png


🖋️默认构造函数


如果我们没写默认构造函数, 就会由编译器自动生成,这是祖师爷制定的规则;假如我们写了,同时也满足默认构造函数的需求,编译器就会以我们写的为准,并转而调用我们写的默认构造函数


注:默认构造函数就是调用时,不需要传参的构造函数


默认构造函数形式一:不带参数

//Date()
  //{
  //  _year = 1970;
  //  _month = 1;
  //  _day = 1;
  //}
  //默认构造函数形式二:参数为全缺省
  Date(int year = 1970, int month = 1, int day = 1)
  {
  _year = year;
  _month = month;
  _day = day;
  cout << "Date(int year = 1970, int month = 1, int day = 1)" << endl;
  }



这里暂时无法给大家演示编译器自动生成并调用的现象

因为祖师爷在设计默认构造函数时,埋下了一个坑:


默认构造函数不对内置数据类型做处理,如 int 、double、char等

至于自定义类型,默认构造函数会去调用属于它们的初始化函数(默认构造函数)

注意:数据类型主要分为两类 <内置类型和自定义类型>


简言之:默认构造函数有点像不干实事的函数


假设我们的类中只有内置类型,那么默认构造函数真就什么都没有做;出现自定义类型时,也只会主动去调用它的默认构造函数,至于自定义类型的默认构造函数干了什么,类的默认构造函数是不管的

9e1f9b7fc0ece8054200dbaa85cdb02.png


因祖师爷设计疏忽而留下的坑,为后世学习C++增加了阻力


这么看来,这个天选之子似乎没有什么用,默认构造函数还得我们自己编写


不过在有些场景下,默认构造函数很有用


题目:用栈实现队列

需要在队列这个类中,调用两个栈类,实现队列类

此时我们只需要写好栈的默认构造函数

队列类的默认构造函数不需要写,因为自动生成的会去调用自定义类型的默认构造函数,即栈的默认构造函数,显然是存在的

为了解决祖师爷留下来的坑,委员会在C++11标准中新增了一个补丁声明时给缺省值


🖋️新增补丁


这个补丁是新标准中的,可能部分老编译器不支持


具体操作很简单:在成员变量声明时,将内置类型给上缺省值,调用编译器生成的默认构造函数时,就会以这些缺省值来初始化成员变量,达到初始化的效果


注意:此时给的是缺省值,并非在声明阶段赋值,类中成员变量为声明阶段


class Date
{
private:
  int _year = 2023; //在内置类型声明时给上缺省值
  int _month = 2; //这样即使调用生成的默认构造函数
  int _day = 9; //也能达到初始化的效果
};

了补丁辅助我们后,就可以看看编译器是否调用了默认构造函数

在打了补丁的情况下,实例化一个对象,可以看到效果如下:


5f4b2b5b4f9d0ed59db439c7c73b1c0.png

C++11中的补丁可以解决内置类型不初始化这个问题,但相对来说,全缺省参数形式的默认构造函数更加实用,不仅能初始化,还能指定初始化值


注意:这个补丁是为内置类型准备的,对于自定义类型,默认构造函数会去调用属于它的默认构造函数


在涉及开辟空间的初始化行为时,可以先给 nullptr,再到默认构造函数体内开辟空间


9c39d7c78da76b5a86db86ecaed494e.png


📖析构函数


析构函数就是另一个天选之子了


构造函数: 解决频繁初始化操作

析构函数: 解决频繁销毁操作


不难看出,这是两兄弟,一个负责做菜,一个负责洗碗

20089125b30ca14f72b8d469e2ab9b3.png


此时仍然是手动挡和自动挡的区别


析构函数和构造函数师出同门,规则也都差不多


析构函数创建规则:


函数名在类名的前面加上~

也没有返回值,连 void 都不需要写

对象声明周期结束时,编译器会自动调用默认析构函数

析构函数不支持重载,毕竟不能对同一个对象销毁多次

析构函数也有一种特殊形式:默认析构函数


不过因为析构函数不支持重载,所以默认与否已经不重要了,如果我们写了,编译器就用我们写的默认析构函数,否则就用编译器自动生成的


默认析构函数也存在默认构造函数的坑:对内置类型不作处理

60f7b648d2c0272246e30533d325c5c.png



🖋️默认析构函数


如果我们没写,编译器会自动生成默认析构函数,假如我们写了,编译器就会用我们写的


注:默认析构函数是在对象生命周期结束时自动调用


class Date
{
public:
  //析构函数写法比较特殊,需要多加注意
  ~Date()
  {
  //假设动态开辟了空间
  //是需要在析构函数中释放的
  _year = _month = _day = 0;
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};


同默认构造函数一样,默认析构函数对内置类型也不处理,对自定义类型,会去调用属于它的默认构造函数


默认构造函数不难写,普通自定义类型是否释放问题不大,但涉及动态内存开辟时,如果不释放内存,就会发生内存泄漏问题,因此当我们的对象涉及动态内存开辟时,需要自己编写默认析构函数,确保安全问题。


~Test()
{
  //假设 pa 为动态开辟的空间
  //需要释放
  free(pa);
  pa = nullptr;
}


📖拷贝构造函数


拷贝构造函数 算是 构造函数 的远房亲戚,因为它们的函数名一样,不过参数不同,构成重载


对于内置类型,我们可以通过 = 完成拷贝,对于自定义类型,规则较多,拷贝需要我们自己完成,因此拷贝构造函数应运而生


为何自定义类型不能直接赋值?

因为自定义类型种类繁多,比如栈、队列、树、图,数据结构很复杂,尤其是涉及空间开辟问题时,不能简单通过指针赋值完成拷贝,这样会导致重复析构问题;正确做法是 开辟空间->拷贝数据->更新指向->完成拷贝


int a = 10;
int b = a;  //内置类型,简单拷贝(赋值)

造函数实现也很简单:


class Date
{
public:
  //拷贝构造函数,此时是简单拷贝,只能用于非动态内存开辟的空间
  //拷贝构造函数函数名与构造函数相同,不过参数类型为类
  Date(const Date& d)
  {
  //d 拷贝给 *this
  _year = d._year;
  _month = d._month;
  _day = d._day;
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};


使用时有以下两种用法:


Date d1;  //将 d1 拷贝给 d2 和 d3
Date d2(d1);  //法一
Date d3 = d1; //法二


既然是天选之子之一,编译器也会生成默认拷贝构造函数


🖋️默认拷贝构造函数


默认拷贝构造函数 是个懂事的函数,对于内置类型,它不再持有偏见,也就是说默认拷贝构造函数能完成简单内置类型的拷贝操作


正常情况下,即成员变量不涉及空间开辟时,拷贝构造函数 没有必要写,用编译器自动生成的足够了


对于涉及空间开辟的,一定要写默认拷贝构造函数


class SeqList
{
public:
  //现在编写一个涉及空间开辟的拷贝构造函数
  SeqList(const SeqList& tmp)
  {
  _pa = (int*)malloc(sizeof(int) * _capacity);
  if (nullptr == _pa)
  {
    cout << "malloc fail" << endl;
    exit(-1); //失败,直接退出程序
  }
  //将 tmp 空间中的值,拷贝到 *this 中
  memcpy(_pa, tmp._pa, sizeof(int) * _size);
  _size = tmp._size;
  _capacity = tmp._capacity;
  }
private:
  int* _pa = nullptr;
  int _size = 0;
  int _capacity = 4;  //动态顺序表
};



默认拷贝构造函数实现比较简单,但有一个值得注意的大问题:无穷递归


🖋️无穷递归


所谓无穷递归问题就是指设计拷贝构造函数时,参数没有设为引用

如下所示:


SeqList(SeqList tmp)
{
  //此时必然会引发无穷递归问题
  //……
}


问题出现原因:值传递,需要先生成临时变量,再传递,而生成临时变量这个行为本身就是在调用拷贝构造函数

也就是说:此时我们在实现拷贝构造函数,但参数又需要拷贝构造函数 这让编译器很难堪

大力士无法举起自己,光靠金针菇也无法完成卡Bug行为


解决方法:因为待拷贝对象本来就已经存在,此时可以使用引用,避免产生临时变量,再加以 const 修饰,保护待拷贝对象


因此正确的拷贝构造函数应该这样写:


SeqList(const SeqList& tmp)
{
  //有效避免无穷递归问题
  //……
}

69cba89121fdd7205a3db62042756fb.png

🖋️浅拷贝


浅拷贝 就是简单的逐字节序拷贝


浅拷贝可能出现空间共用的情况


483ff85d991c842aa22998d3507b7a5.png

浅拷贝可能引发对同一块空间的重复析构问题

10d592a73c1da42e8b69bba28da03ef.png

浅拷贝不可取,尤其是在面对复杂数据结构时


🖋️深拷贝


深拷贝需要我们自己实现

深拷贝 在面对空间问题时,会先给 对象2 开辟一块同样大的空间,再将 对象1 空间中的数据拷贝过来


深拷贝中,两个对象的空间是独立的、互不干扰的

02c78439701c648486dd7bb81c6c59e.png


深拷贝才是众望所归

2e7eef100fc4b10fd4f2a787bd885c9.png

当成员涉及复杂数据结构、空间开辟时,就需要写出默认拷贝构造函数


🖋️小结


构造函数家族至此就介绍完成了,简单小结一下:


类型 用途 处理情况
默认构造函数 初始化对象 不对内置类型作处理
默认析构函数 销毁对象 也不对内置类型作处理
默认拷贝构造函数 拷贝对象 只能对简单内置类型做处理

何时需要自己写默认析构函数?


当我们写出默认拷贝函数完成复杂对象的拷贝时,就证明需要默认析构函数来释放对象

小技巧:


在函数传参与返回时,如果对象生命周期足够长,就可以考虑使用引用的方式,避免参数走拷贝构造->生成临时变量->再传递的路线,提高程序运行效率

特别注意: 默认拷贝构造函数与默认构造函数名相同,当我们只写拷贝而不写构造时,编译器就会报错,因为此时的拷贝会被误以为是默认构造函数

也就是说,默认拷贝构造函数存在的前提是默认构造函数已存在


📖运算符重载


C++支持运算符重载,运算符重载使得自定义类型间的符号运算变成可能

比如:


int a = 1;
int b = 2;
a - b;  //合法
Date d1;
Date d2;
d1 - d2;  //非法,此时需要通过运算符重载解决这个问题
//解决方法如下
const Date operator-(const Date& d)
{
  //简单演示,逻辑存在问题,可以忽略
  Date tmp(*this);  //调用拷贝构造
  tmp._year -= d._year; //内置类型,可以正常相减
  tmp._month -= d._month; //同理
  tmp._day -= d._day; //同上
  return tmp; //tmp 为临时变量,不能传引用返回
}


此时就可以正常使用 d1 - d2 了


注:运算符重载和函数重载没有关系


🖋️operator操作符


operator 译为运算符,是C++中新的关键字,operator 的作用很简单,实现自定义类型的运算


使用规则:


operator 函数中的操作数取决于参数个数

operator 一般写在类中,方便通过 this 指针访问成员变量

写在类中时,this 指针就算一个隐藏参数

operator也可以写在类外,此时会发生无法访问成员变量问题,可以这样解决:

将成员变量设为 public (不安全)

通过函数获取类中的成员变量值 (麻烦)

设置为友元函数(也比较麻烦)

写在类中,最简单、省事,而且还可以使用 this 指针

运算符重载是这样用的:


int main()
{
  //注:此时只是演示,日期类的减法不能这样写
  Date d1(2023, 2, 9);
  Date d2(2022, 2, 9);
  Date d3 = (d1 - d2);  //结果为 1 0 0
  //此时的调用相当于 d1.operator-(d2) 
  //d1 作为 this 对象传递
  //也可以这样使用
  Date d4 = operator-(d1, d2);
  return 0;
}


基于运算符重载,我们可以干很多事情,比如直接通过 [] 访问类中的成员,实现两个对象的快速运算等操作


🖋️使用注意


operator 虽然很好,但也有很多使用规则:


operator 操作符就是函数名

不能与非操作符链接

参数中必须有一个自定义类型

对于内置运算符,不能改变其含义

成员函数的第一个参数为 this

有五个运算符不支持重载:

.* 稀有运算符,很少见

:: 域作用限定符

sizeof 操作符

? : 三目运算符

. 访问成员符

为何运算符能实现重载?


跟函数重载同理,保证函数修饰名不同,构成重载

下面是我测试出的部分运算符重载修饰规则:

c2141fd447fb9349b65d8a458cbfa93.png

基于运算符重载,我们可以介绍第四个天选之子:赋值重载函数


📖赋值重载函数


赋值重载函数的实现原理就是运算符重载

此时重载的运算符是 =


赋值重载的目的:将 d1 对象赋值给 d2,非拷贝构造,d1、d2均已存在


class Date
{
public:
  //赋值重载函数
  Date& operator=(const Date& d)
  {
  //能用引用的地方,就用引用
  //避免去走拷贝构造函数
  //如果传入的是两个相同值,没必要再执行赋值
  if (this == &d)
  {
    return *this;
  }
  _year = d._year;
  _month = d._month;
  _day = d._day;
  //返回赋值完成的值,即 *this
  return *this;
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};


为何传引用?


两个对象都已存在,使用引用,提高效率

为何判断相同?


避免资源浪费,当类的成员变量很多时,假如出现 d1 = d1 = d1 这种情况时,可以有效避免资源浪费

为何返回 *this ?


因为可能出现重复赋值的情况,如 d1 = d2 = d3

赋值重载函数不难实现,只是需要注意的地方很多


🖋️默认赋值重载函数

祖师爷在实现默认赋值重载函数时,实现的几乎已经很好了,无论是内置类型还是自定义类型,都会处理


不过默认赋值重载函数仍然是基于字节序的浅赋值,在面对空间开辟时,仍然需要我们自己编写深度赋值重载函数,否则就会发生重复析构问题


🖋️深度赋值

深度赋值的实现和深拷贝几乎一模一样,这里就不加以赘述


523e4f4dbf5d2ccbaad6921fab0308e.png

一但对象中涉及动态内存开辟,必须自己实现深度拷贝


🖋️注意事项

拷贝构造 和 赋值重载存在本质区别,一个是对象尚未实例化,另一个是两个对象都已存在


当两个对象都被创建,并发生赋值行为时,才叫做赋值重载


Date d1;
Date d2(d1);  //拷贝构造,d2 未存在
Date d3 = (d1 - d2);  //赋值重载,d1、d2 已存在


涉及空间资源管理时,必须实现深度拷贝


设计赋值重载函数时,充分利用引用,提高效率


📖const修饰


const 修饰可以提高程序的健壮性


const 常被用来修饰引用、指针


当被指向对象为常量或临时变量时,必须使用 const 修饰,避免出现权限放大问题


//int* pa = 10; //错误,10 具有常性
const int* pa = (const int*)10; //成功,此时 pa 为常量指针
//int& pb = 20; //错误,20 具有常性
const int& pb = 20; //成功引用,此时 pb = 20


const 一般用来修饰指针参数或引用参数,确保参数在使用过程中不被修改


🖋️修饰*this


引入:两个日期 d1、d2,d1 - d2 时,d1 需要被修改,但 d2 不能被修改,因此实现 operator- 时,参数 d 为 const Date& 类型


我们在实现函数时,存在这种情况 确保 *this 不被修改,即 this 指针指向内容不被修改


this 指针太危险了,如果不加以保护的话,可能实现者的不经意行为会导致严重的后果


class Date
{
public:
  //实现简单的打印函数
  void Print()
  { 
  cout << _year << "年";
  cout << _month << "月";
  cout << _day << "日";
  cout << endl
  //有可能不小心出现这样的情况
  _year = _month = _day;
  //此时该对象就危险了,成员变量全被改了
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};



this 指针给了我们足够的自由,我们在使用时也应该尊重它


class Date
{
public:
  //实现简单的打印函数
  //原指针类型为 Date* const ,只允许指向对象
  //修改指针类型为 const Date* const 双重保护
  void Print() const
  { 
  cout << _year << "年";
  cout << _month << "月";
  cout << _day << "日";
  cout << endl
  //有可能不小心出现这样的情况
  _year = _month = _day;  //此时会报错,因为 this 指针类型为 const Date* const
  //此时该对象就危险了,成员变量全被改了
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};



除了上述情况外,还可能存在这种情况:


const Date d1;
//此时 d1 具有常性,普通的 this 指针无法调动,需要使用 const 指针
1

2

总之,const可以修饰this指针,起到保护和权限平移交接的效果


📖取地址重载函数


接下来简单介绍一下剩下两个天选之子


取地址重载函数


获取当前对象的地址

class Date
{
public:
  Date* operator&()
  {
  return this;
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};


📖const修饰的取地址重载函数


const修饰的取地址重载函数


获取 const 修饰对象的地址

class Date
{
public:
  const Date* operator&() const
  {
  return this;
  }
private:
  int _year = 2023;
  int _month = 2;
  int _day = 9;
};


这两个默认成员函数都很简单,使用编译器默认生成的就够了,除非我们不想让别人获取到当前地址,直接手动设置,每次都返回 nullptr ,当然这种情况几乎不存在


开发者何必为难开发者


📘总结


以上就是关于 类和对象(中) 的全部内容了,本文主要侧重点为 六大天选之子,以及编译器自动生成的默认成员函数,何时用编译器的、何时用我们自己写的,都是有讲究的,部分成员函数规则多、实现麻烦,需要多加练习以加深理解。这里推荐日期类的实现练习,能让我们对类和对象有一个更深层次的理解,关于日期类的实现,我将会在下篇文章中介绍,敬请期待!


如果你觉得本文写的还不错的话,期待留下一个小小的赞👍,你的支持是我分享的最大动力!


如果本文有不足或错误的地方,随时欢迎指出,我会在第一时间改正


目录
相关文章
|
14天前
|
C++
C++(十一)对象数组
本文介绍了C++中对象数组的使用方法及其注意事项。通过示例展示了如何定义和初始化对象数组,并解释了栈对象数组与堆对象数组在初始化时的区别。重点强调了构造器设计时应考虑无参构造器的重要性,以及在需要进一步初始化的情况下采用二段式初始化策略的应用场景。
|
14天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
14天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
14天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
27天前
|
存储 算法 编译器
c++--类(上)
c++--类(上)
|
1月前
|
编译器 C++
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
virtual类的使用方法问题之C++类中的非静态数据成员是进行内存对齐的如何解决
|
1月前
|
编译器 C++
virtual类的使用方法问题之静态和非静态函数成员在C++对象模型中存放如何解决
virtual类的使用方法问题之静态和非静态函数成员在C++对象模型中存放如何解决
|
1月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
|
14天前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
23天前
|
存储 C++
C++ dll 传 string 类 问题
C++ dll 传 string 类 问题
16 0