lesson3-C++类和对象(下)(一)

简介: lesson3-C++类和对象(下)

再谈构造函数

构造函数体赋值

class Date
{
public:
  Date(int year = 2023, int month = 10, int day = 30)
  {
    _year = year;
    _month = month;
    _day = day;
  }
private:
  int _year;
  int _month;
  int _day;
};

我们通过构造函数可以给对象赋值,但是不能称为初始化,因为初始化只能初始化一次,而构造函数函数体中可以进行多次赋值。

初始化列表

class Date
{
public:
  Date(int year = 2023, int month = 10, int day = 30)
    :_year(year)
    ,_month(month)
    ,_day(day)
  {}
private:
  int _year;
  int _month;
  int _day;
};

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟

一个放在括号中的初始值或表达式。

下面的大括号里还可以进行一系列赋值或者其他操作,相当于是构造。

注意:

  1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)。
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
  • 引用成员变量
  • const成员变量
  • 自定义类型成员(且该类没有默认构造函数时)
class A
{
public:
  A(int a = 0)
    :_a(a)
  {}
private:
  int _a;
  int _b;
};
class B
{
public:
  B(int& a,int b,A& c)
    :_a(a)
    ,_b(b)
    ,_c(c)
  {}
private:
  int& _a;
  const int _b;
  A _c;
};
int main()
{
  int a = 6;
  int b = 3;
  A c;
  B b(a,b,c);
  return 0;
}

  • f具有常属性,不可修改。
  • 引用在声明时就需要初始化,而且之后不可以修改。
  • 自定义类型在没有默认构造函数的时候,我们有没有给参数,或者说他在类里声明,我们都需要把他加进初始化列表中进行初始化。

3.尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,

一定会先使用初始化列表初始化。

其实也就是不管是内置类型还是自定义类型,都会先去调用他的默认构造或者构造函数,在进入构造函数前,先走初始化列表,不管我们是否写了初始化。

class A
{
public:
  A(int num = 0)
    :_a(num)
  {}
  A(const A& _A)
  {
    _a = _A._a;
    _b = _A._b;
  }
  int Getnum_a()
  {
    return _a;
  }
private:
  int _a;
  int _b;
};
int main()
{
  //会先去调用默认构造函数,然后在构造之前走初始化列表
  //将a._a初始化为0
  A a;
  cout << a.Getnum_a() << endl;
  return 0;
}

4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后

次序无关

class A
{
public:
    //这里初始化顺序不是按先写就先初始化    
    //而是按照声明顺序进行初始化
  A(int a)
    :_a2(a)
    ,_a1(_a2)
  {}
  void print() const
  {
    cout << _a1 << " " << _a2 << endl;
  }
private:
  int _a1;
  int _a2;
};
int main()
{
  A a(1);
  a.print();
  return 0;
}

explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者多个参数的构造函数,还可以通过赋值类型转换的方式构造对象。

解释在代码注释里

class Date
{
public:
  Date(int year)
    :_year(year)
  {}
  Date(int year, int month, int day)
    :_year(year)
    ,_month(month)
    ,_day(day)
  {}
  void print() const
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
    //C++支持单个参数的构造函数这样使用
  //先用2023去构造临时对象,该临时对象再进行和对象b的拷贝构造
  Date date1 = 2023;
  date1.print();
    //C++11支持多参数这样使用,和上面是相同的道理
  Date date2 = { 2023,10,30 };
  date2.print();
  return 0;
}

class Date
{
public:
  Date(int year)
    :_year(year)
  {}
  Date(int year, int month, int day)
    :_year(year)
    ,_month(month)
    ,_day(day)
  {}
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
  void print() const
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date date1 = 2023;
  date1.print();
  Date date2 = { 2023,10,30 };
  date2.print();
  const Date date3 = { 2023,10,31 };
  date3.print();
  return 0;
}

但是这样写可读性不好,有没有看着别扭的感觉,所以如果我们不想让这样的方式能通过编译,就使用explicit关键字修饰该构造函数。

class Date
{
public:
  explicit Date(int year)
    :_year(year)
  {}
  explicit Date(int year, int month, int day)
    :_year(year)
    , _month(month)
    , _day(day)
  {}
  void print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
  void print() const
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date date1 = 2023;
  date1.print();
  Date date2 = { 2023,10,30 };
  date2.print();
    //用这三个数去构造一个临时对象,然后拷贝构造
  const Date date3 = { 2023,10,31 };
  date3.print();
  return 0;
}

加上explicit后就不允许这样构造对象了。

我们再举一个其他栗子:

class Date
{
public:
  Date(int year,int month,int day)
    :_year(year)
    ,_month(month)
    ,_day(day)
  {
    cout << "Date()" << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  const Date& a = { 2023,11,2 };
  return 0;
}

解释:我们用三个参数去构造一个临时对象,a就是这个临时变量的别名,如果我们不加const就会报错,因为临时变量具有常性,而我们先前说过权限放大和缩小的问题,一个const类型的对象被非const类型引用,会出现权限放大,所以我们要加上const。

Static成员

我们先来抛出一个问题,假如我们想要计算一共有几个对象被构造,那么如何计算?我们有如下几个方案,判断他们的可行性。

方案一:搞一个全局变量,在每一次的调用构造函数时++。

方案二:在类里定义一个计数的变量。

接下来我们来看他们是否可行:

先看方案一实现的代码:

#include <iostream>
using namespace std;
int count = 0;
class A
{
public :
  A()
    :_a(1)
    ,_b(2)
  {
    count++;
  }
  void print() const
  {
    cout << _a << " " << _b << endl;
  }
private:
  int _a;
  int _b;
};
void func(A a)
{
  //...
}
int main()
{
  A a;
  func(a);
  return 0;
}

结果不明确是因为我们使用了using namespce std;展开了该命名空间,而该命名空间里有函数的名字也叫做count,所以就出现了结果不明确,当然,我们可以换个名字,不是非要用count,而且,我不展开std命名空间不可以吗,我只展开部分,比如cout或者endl展开就好,难道不可以吗?是的,都可以。

接着看方案二:

class A
{
public:
  A()
    :_a(1)
    , _b(2)
  {
    count++;
  }
  void print() const
  {
    cout << _a << " " << _b << endl;
  }
private:
  int _a;
  int _b;
  int count;
};
void func(A a)
{
  //...
}
int main()
{
  A a;
  func(a);
  return 0;
}

这样可以吗?显然不行,每个对象都有count,这样的话,不管哪个count,都只是1,都只构造一次。

那么有没有更好的解决方案呢?有的,就是我们接下来要说的static成员。

lesson3-C++类和对象(下)(二)+https://developer.aliyun.com/article/1393915

目录
相关文章
|
13天前
|
C语言 C++
从C语言到C++⑧(第二章_类和对象_下篇_续)笔试选择题和OJ题
从C语言到C++⑧(第二章_类和对象_下篇_续)笔试选择题和OJ题
18 0
|
13天前
|
Java 编译器 C语言
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(下)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
9 0
|
13天前
|
C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(中)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
24 0
|
13天前
|
编译器 C语言 C++
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象(上)
从C语言到C++⑦(第二章_类和对象_下篇)初始化列表+explicit+static成员+友元+内部类+匿名对象
12 1
|
13天前
|
编译器 C语言 C++
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题(下)
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题
25 2
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题(下)
|
13天前
|
编译器 C语言 C++
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题(中)
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题
22 1
|
13天前
|
算法 编译器 C语言
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题(上)
从C语言到C++⑥(第二章_类和对象_中篇_续)大练习(日期类)+笔试选择题
24 3
|
13天前
|
编译器 C语言 C++
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)(下)
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)
8 1
|
13天前
|
编译器 C语言 C++
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)(中)
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)
11 0
|
13天前
|
编译器 C语言 C++
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)(上)
从C语言到C++⑤(第二章_类和对象_中篇)(6个默认成员函数+运算符重载+const成员)
9 0