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

一、再谈构造函数

1.初始化列表

1.1构造函数体赋值

例如:

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

private:
    int _year;
    int _month;
    int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

1.2初始化列表初始化

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

例如:

class Date
{
public:
    Date(int year, int month, int day): _year(year), _month(month), _day(day)
    {}

private:
    int _year;
    int _month;
    int _day;
};

【注意】

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2. 类中包含以下成员,必须放在初始化列表位置进行初始化:引用成员变量、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
};

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

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

例如:

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();
}

A.输出1 1

B.程序崩溃

C.编译不通过

D.输出1 随机值

1.3隐式类型的转换

1.3.1单参数

例如:

1.3.2多参数

例如:

int main()
{
     A aa1(1,1);            //构造函数
    A aa2 = {2,2};          //隐式类型转换

    return 0;
}

二、Static成员

类的静态成员包括静态成员变量和静态成员函数。

1.静态成员变量

类内声明,类外定义并初始化(声明是有static修饰,定义时没有static);不属于某个对象,属于所有对象,属于整个类;存放在静态区;若静态成员变量是私有的,则在类域外无法访问,若是共有的,则可以通过类名::变量名或者对象名::变量名进行访问。

例如:

class Date
{
     //...成员函数

private:
    static int count;//类内声明
    int a;
    int b;
};

int A::count = 0;//类外定义并初始化

注意:静态成员变量也是成员变量,除静态成员函数外的其他成员函数也有权对其进行访问或修改。

2.静态成员函数

函数形参没有隐藏的this指针,所以在静态成员函数中不能访问非静态成员变量;主要功能是对静态成员进行操作。

【问题】

1. 静态成员函数可以调用非静态成员函数吗?

2. 非静态成员函数可以调用类的静态成员函数吗?

三、友元

1.友元函数

定义:friend 关键字修饰的函数,是特殊的全局函数,可以访问其声明的类域的类的成员变量。

友元函数可以访问类内的所有成员变量,破坏了类的封装性,必要时可以使用,但尽量少用。

问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的

输出流对象和隐藏的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作

数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成

全局函数但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。

例如:

Class Date
{
     //<< 函数重载
    friend ostream& operator<<(ostream& _cout, const Date& d);

    //>> 函数重载
    friend istream& operator>>(istream& _cin, Date& d);

private:
    int _year;
    int _month;
    int _day;
};

//<< 函数重载
ostream& operator<<(ostream& _cout, const Date& d)
{
    _cout << d._year << '-' << d._month << '-' << d._day;

    return _cout;
}

//>> 函数重载
istream& operator>>(istream& _cin, Date& d)
{

    _cin >> d._year >> d._month >> d._day;

    return _cin;
}

2.友元类

友元类内的所有成员函数都可以访问类内的成员变量,尽量不要使用。

特点:①友元关系不具对称性(单向)例如:A是B的友元类,B不是A的友元类

           ②友元关系不具传递性    例如:A是B的友元类,B是C的友元类,但A不是C的友元类

           ③友元关系不可继承

四、内部

一个类的定义在另外一个类内,如:类B的定义在类A内,则类B是一个内部类。

特点:(以上例为例)

          内部类B和外部类类A是两个独立的类,此处的独立是指物理空间的独立;

          内部类B受外部类A的类域限制;

          如果内部类B是外部类A的私有成员,则在类外不可以访问内部类B。

          内部类B天生是外部类A的友元类,B的成员函数可以访问A的所有成员变量。

例:

class A
{
public:
    A(int a = 1) :_a(a) {}

    class B
    {
    public:
        B(int b = 2) :_b(b) {}

        void Print_A(const A& a)
        {
            cout << a._a << endl;
        }

    private:
        int _b;
    };

private:
    int _a;
};

int main()
{
    A aa;
    //内部类和外部类物理空间独立
    cout << sizeof(aa) << endl;//4

    A::B bb;
    //内部类天生是外部类的友元类,可以访问外部类的成员变量
    bb.Print_A(aa);//1

    return 0;

}

五、匿名对象

匿名对象,顾名思义,即没有名字的对象。

特点:1.匿名对象的生命周期只有一行代码,执行完后就销毁

          2. 特殊情况下,有的类对象只需使用一次,则可以使用匿名对象(类比一次性物品来理解)

格式:类名()

例如:

class A
{
public:
    A(int a = 0):_a(a)
    {
        cout << "A(int a)" << endl; 
    }

    ~A()
    {
        cout << "~A()" << endl;
    }

private:   
    int _a;
};

class Solution 
{
public:
    int Sum_Solution(int n) 
    {
        //...
        return n;
    }
};

int main()
{
    A aa1;

    //A aa1();// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义


    A();// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,

    // 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数


    A aa2(2);
 
    Solution().Sum_Solution(10);// 匿名对象在这样场景下就很好用,当然还有一些其他使用场 
                                   景,这个我们以后遇到了再说

    return 0;
}

六、拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的。

1.构造+拷贝构造====优化====>构造

2.拷贝构造+拷贝构造====优化====>拷贝构造

3.构造+拷贝构造+拷贝构造====优化====>构造

例如:

class A
{
public:
    //构造函数
    A(int a = 0):_a(a)
    {
        cout << "A(int a)" << endl;
    }

    //拷贝构造函数
    A(const A& aa):_a(aa._a)
    {
        cout << "A(const A& aa)" << endl; 
    }

    //赋值重载函数
    A& operator=(const A& aa)
    {
        cout << "A& operator=(const A& aa)" << endl;
        if (this != &aa)
        {
            _a = aa._a;
        }
        return *this;
     }

    //析构函数
    ~A()
    {
        cout << "~A()" << endl;
    }

private:
    int _a;
};

void f1(A aa)
{}

A f2()
{
    A aa;

    return aa;
}

int main()
{
    A aa1;  
    f1(aa1);//传值传参  
    cout << endl;
  
    f2();  // 传值返回
    cout << endl;
 
    f1(1);// 隐式类型,构造+拷贝构造->优化为直接构造

    f1(A(2));// 一个表达式中,构造+拷贝构造->优化为一个构造
    cout << endl;

    A aa2 = f2();// 一个表达式中,拷贝构造+拷贝构造->优化一个拷贝构造
    cout << endl;

    aa1 = f2();// 一个表达式中,拷贝构造+赋值重载->无法优化
    cout << endl;

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值