AI智能
改变未来

C++入门篇(5)之类和对象总结

文章目录

  • 前言
  • 再谈构造函数
  • 初始化列表
  • 初始化列表注意点
  • explicit关键字
  • static成员
    • 静态成员为所有类对象所共享,不属于某个具体的实例
    • 类静态成员即可用类名::静态成员或者对象.静态成员来访问
    • 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
    • 静态成员和静态函数也有public、protected、private3种访问级别,也可以具有返回值
  • 友元
    • 友元函数
    • 友元类
  • 内部类
  • 前言

    本篇文章将会主要讲解构造函数的初始化列表,static成员,以及内部类,目的是对前几章讲解类时候的一个深入和总结.

    再谈构造函数

    我们上一节讲解构造函数时候,了解了构造函数的定义和使用,明白编译器会通过调用构造函数给类成员一个初始值,但是注意了~~,这个过程却并不是初始化,而是只能叫做赋初值,因为初始化只能一次,比如下面的一段代码:

    class A {public:A(int c = 10) {cout << "A(int c)" << endl;_c = c;}private:int _c;};class B {public:B(int a,int b,A& ca) {_a = a;_b = b;_ca = ca;}private:int _a;int _b;A _ca;};int main() {A a(20);B b(1,2,a);return 0;}

    大家觉得上面的结果会是什么?没错,会打印两句

    A(int c)

    ,但是博主在这里有个小小问题,就这两句分别在哪一行代码后打印的呢?

    答案:

    • 第一次定义A对象后,会调用一次构造函数,也就打印了第一句
      A(int c)

      .

    • 第二次打印确是在程序
      B(int a,int b,A& ca)

      之后,程序

      _a = a

      之前,也就是在这两句之间,不信吗?我们下图为例:

    当程序执行到

    _a = a

    以后,已经打印出了

    A(int c)

    ,那么这是为什么呢? 请看下一小节的初始化列表.

    初始化列表

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

    什么意思呢?如下:

    class Date {public:Date(int year, int month, int day):_year(year),_month(month),_day(day){}void Print(){cout<<"year是"<<_year<<endl;cout<<"month是"<<_month<<endl;cout<<"day是"<<_day<<endl;}private:int _year;int _month;int _day;};int main(){Date date(2021,10,23);date.Print();return 0;}

    我们可以清晰地看到,通过构造函数的初始化列表,就已经成功初始化了类数据成员.所以现在大家应该已经明白了,为什么说初始化列表才是真正的初始化数据成员,而通过构造函数的函数体赋值形式叫做初次赋值.因为在函数体内部的赋值性质是可以修改初始化列表的结果,比如把构造函数改成下面这样:

    Date(int year, int month, int day):_year(year),_month(month),_day(day){_year = 2000;_month = 1;_day = 1;}

    那么刚才的结果将会是这样:

    初始化列表注意点

    ①每个数据成员只能在初始化列表中出现一次(因为只能初始化一次),还是按照上面的Date类为例,若这样写就错误了:

    Date(int year, int month, int day):_year(year),_month(month),_day(day),_year(2000),_month(10),_day(20){}

    ②数据初始化顺序只和数据声明顺序有关,与初始化列表无关:

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

    该类的结果为:

    我们能够发现,

    _a2

    的值并不是100,因为最开始初始化的数据就是

    _a2

    ,但是给

    _a2

    的值缺是还没有初始化的

    _a1

    ③当类中含有以下三种数据成员时候,对他们初始化必须使用初始化列表,分别是:

    • 引用成员变量
    • const成员变量
    • 自定义类型成员变量(没有默认的构造函数时候)
    class A{public:A(int a):_a(a){}private:int _a;};class B{public:B(int a, int ref):_aobj(a),_ref(ref),_n(10){}private:A _aobj; // 没有默认构造函数int& _ref; // 引用const int _n; // const};

    explicit关键字

    他的作用说简单点就是不允许定义对象时候使用

    =

    形式初始化,比如:

    class Date {public:explicit Date(int n){_n = n;}private:int _n;};int main(){Date date1(100);Date date2 = 100; //正常情况下,这样也是可以初始化的,但是加了explict就不可以.return 0;}

    static成员

    概念:主要有两种,一种是修饰数据成员,称为静态成员变量.一种是修饰成员函数,称为静态成员函数.其中静态成员变量必须在类外进行初始化,并且静态成员大小不计算在类内.

    其特性为:

    静态成员为所有类对象所共享,不属于某个具体的实例

    class Date {public:Date(int b  = 10){_a++;_b = b;}static void cntans(){cout<<_a<<endl;}private:static int _a;int _b;};int Date::_a = 10;   //静态成员必须在外面初始化.int main(){Date d1;Date d2;Date d3;d1.cntans();d2.cntans();d3.cntans();return 0;}

    大家猜猜会打印什么呢?答案如下:

    可以清晰的看到,三次打印都是13,原因就是因为静态成员由所有类对象共享,并不是属于某个具体对象

    类静态成员即可用类名::静态成员或者对象.静态成员来访问

    在上面我们已经使用过了

    对象.静态成员访问

    格式,现在就介绍一下

    类名::静态成员

    .

    仍然以上面为例:

    int main(){Date d1;Date::cntans();    //类名::静态成员格式return 0;}

    静态成员函数没有隐藏的this指针,不能访问任何非静态成员

    我们仍然以上面Date类为例,比如下面的修改cntans函数的错误形式:

    static void cntans(){cout<<_a<<endl;  //_a是静态成员,类内访问没问题.cout<<_b<<endl; //但是_b是非静态成员,由于没有this指针,所以访问_b非法.}

    静态成员和静态函数也有public、protected、private3种访问级别,也可以具有返回值

    这个博主就不再赘述,大家自行检查.

    友元

    友元和静态很相似,具有两种.修饰函数的叫做友元函数,修饰类的叫做友元类.在我们介绍这个之前,先做一个小测验吧,我们利用之前学过的重载运算符,对

    <<

    或者

    >>

    进行重载,实现的类仍然是我们上面的日期类

    友元函数

    class Date{public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}ostream& operator<<(ostream& out){out<<_year<<"-"<<_month<<"-"<<_day;return cout;}private:int _year;int _month;int _day;};

    大家觉得我们这样进行重载,会不会有什么问题呢?我们先调用一下试试吧.

    Date date(2021,10,10);cout << date;

    我们进行编译,运行然后就能发现结果报错:

    为什么呢?我们之前在讲运算符重载时候强调过,我们在写参数列表时,需要按照运算符的操作数格式进行重载.但是这里呢>我们按照了吗,并没有,因为这里有隐藏的this指针,也就是说操作数弄反位置了.那怎么进行修改呢?目前我们的办法只有一个,那就是放到全局:

    ostream& operator<<(ostream& out,Date& date){out << _year << "-" << _month << "-" << _day;return cout;}

    但是像这样又会遇到一个问题,那就是

    _year

    等成员是私有的,在外部无法访问.那又怎样进行解决呢?现在就是我们的老大哥—

    friend

    友元驾临.

    这种情况我们只需要在类Date中的任何位置放一份重载函数声明,并在前面加上

    friend

    .

    class Date{friend ostream& operator<<(ostream& out,Date& date);  //一般习惯性的是加在这里public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;};

    注意:友元函数,只需要在函数的声明前加上

    friend

    就行,并且友元不属于任何类,只是为了突破类的作用域限制,以达到访问类的私有或保护成员.

    并且友元函数不可以用

    const

    进行修饰,不受任何的类作用域符限制,而同一个友元函数还可以是多个类的友元.

    友元类

    友元类和友元函数相似,当一个类A是类B的友元类后,类A便可以访问B的任何成员和函数.使用方法和友元函数一模一样,不再介绍.

    内部类

    概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。注意此时这个

    内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去调用内部类

    。外部类对内部类没有任何优越的访问权限。
    注意:内部类就是外部类的友元类。注意友元类的定义,

    内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元

    .

    特性:

    1. 内部类可以定义在外部类的public、protected、private都是可以的。
    2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
    3. sizeof(外部类)=外部类,和内部类没有任何关系
    class A{private:static int k;int h;public:class B{public:void foo(const A& a){cout << k << endl;//cout << a.h << endl;//   //想要访问外部类的其他成员,必须通过外部类的对象参数.}};};int A::k = 1;int main(){A::B b;        //定义内部类,只能通过作用域限制符.b.foo(A());return 0;}
    赞(0) 打赏
    未经允许不得转载:爱站程序员基地 » C++入门篇(5)之类和对象总结