【C++】类和对象(下)(一)

简介: 【C++】类和对象(下)(一)

1.再谈构造函数


在之前的类和对象(中)我们了解到了构造函数,在创建对象的时候编译器通过调用构造函数,给对象中的各个成员变量赋一个合适的初始值,但是:

class A
{
public:
  A(int a)
    :_a(a)
  {}
private:
  int _a;
};
class B
{
public:
  B(int a, int ref)
  {}
private:
  A _aobj;
  int& _ref;
  const int _n;
};


运行这段代码发现,这种写法是有问题的,会报错

faf90f9762506c6aecad99fb24bf273b.png


这是因为在调用构造函数给成员变量赋值的过程本质不是变量的初始化,而是变量的赋值,而变量的初始化是在初始化列表进行的


1.1初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

以Date类为例:

07657b96d00d592e5c77dab828e345b9.png

那么,使用初始化列表需要注意什么?/ 初始化列表的特点是什么?


  1. 每个成员变量在初始化列表中只能出现一次;
  2. 类中包含以下成员的时候:
  1. 引用成员变量,
  2. const修饰的成员变量,
  3. 自定义类型成员(且该类没有默认构造函数时);
  1. 尽量使用初始化列表初始化;
  2. 成员变量在初始化列表中的初始化顺序是成员变量在类中的声明次序,与初始化列表中的次序无关;


特点讲解

特点一:初始化列表的本质是定义初始化成员变量,初始化只能有一次。

特点二:引用和const修饰的变量在定义的时候必须初始化,并且后面不能再修改;实例化对象的时候,会自动调用默认构造函数,如果没有默认构造函数的话就需要传参,这里括号内的就是参数。

特点三:不管那种类型的成员变量,都是需要走一遍初始化列表的,出于效率的考虑,我们建议使用初始化列表初始化。

特点四:下面这段代码的运行结果是1 随机值。

class A
{
public:
  A(int a)
   :_a1(a)
   ,_a2(_a1)
 {}
  void Print() {
    cout<<_a1<<" "<<_a2<<endl;
 }
private:
  int _a2;
  int _a1;
};
int main() {
  A aa(1);
  aa.Print();
}

1.2explicit关键字

explicit的作用:修饰构造函数,禁止(隐式)类型转换。

我们还是以日期类为例:

class Date
{
public:
  Date(int year)
    :_year(year)
    ,_month(1)
    ,_day(1)
  {}
  Date& operator=(const Date& d)
  {
    if (this != &d)
    {
      _year = d._year;
      _month = d._month;
      _day = d._day;
    }
    return *this;
  }
  void Print()
  {
    cout << _year << " " << _month << " " << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1(2022);
  d1.Print();
  d1 = 2023;
  d1.Print();
  return 0;
}


这段代码是可以正常运行的,原理是在d1.Print() = 2023这句代码中,使用常量2023创建了一个类型为Date的临时变量,然后将这个临时变量拷贝给d1。但是如果在构造函数前面加上explicit就禁止了这个类型转换,所以代码将不能运行。

9a43ec6667f2ff2e710d7953795ba6ff.png


2.static成员


2.1概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数静态成员变量一定要在类外进行初始化


在这里分享一个面试题:实现一个类,计算程序中创建出了多少个类对象

读到这个题目,我们首先先到的应该就是使用一个全局变量或者静态变量,每次创建新的变量的时候使该变量自增。但是使用全局变量的风险是很大的,因为全局变量在任意位置都可以修改,所以我们考虑把这个静态变量放在类里面。

class A
{
public:
  A()
  {
    N++;
  }
  A(const A& t)
  {
    N++;
  }
  static int GetN()
  {
    return N;
  }
private:
  static int N;
};
int A::N = 0;
void TestA()
{
  cout << A::GetN() << endl;
  A a1, a2;
  A a3(a1);
  cout << A::GetN() << endl;
}


2.2特性

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用类名::静态成员或者对象.静态成员来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制
相关文章
|
1天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
14 2
|
8天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
33 5
|
14天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
46 4
|
15天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
43 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
1月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
17 0
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)