类与对象3——初始化列表,static成员

本文探讨了C++中构造函数的作用,特别是当自定义类型成员没有默认构造函数时的问题。介绍了初始化列表的概念,用于解决成员初始化的问题,并强调了初始化列表的顺序重要性。同时,讲解了`explicit`关键字防止隐式类型转换的应用,以及`static`成员的特性,包括静态成员变量的初始化和静态成员函数的使用。通过实例解析了如何利用静态成员解决特定的编程问题。
摘要由CSDN通过智能技术生成

        我们上次说到了,构造函数是为了初始化我们的对象,而非在内存中开辟一个对象。而默认构造函数的特点是对内置类型不做任何的处理,对自定义类型而言,会去调用它的默认构造函数。

        那如果自定义类型成员也没有默认构造函数呢?比如说它是半缺省的构造函数。 这时候编译器就会报错,因为没有合适的默认构造函数可以调用。

class Time
{
public:
	Time(int hour, int min) //自己实现一个非默认的构造函数
	{
		_hour = hour;
		_min = min;
	}
private:
	int _hour;
	int _min;
};
class Date
{
public://Date没有默认的构造函数
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d1;
	return 0;
}

一、初始化列表 

        一种解决方法是,在Date中实现非默认的构造函数,在里面对_t的成员进行初始化,但是这样避免不了要先构造一个新的Time类型的成员,然后对_t进行拷贝构造。

        但是这样太麻烦了,C++为了解决这种情况,引入了初始化列表!我来为大家写一个初始化列表,再仔细地介绍它的写法。

class Time
{
public:
	Time(int hour, int min) //自己实现一个非默认的构造函数
	{
		_hour = hour;
		_min = min;
	}
private:
	int _hour;
	int _min;
};
class Date
{
public:
    Date()
        :_year(1)
        ,_month(2)
        ,_t(1,2)
    {
    }
private:
	int _year;
	int _month;
	int _day;
	Time _t;
};

int main()
{
	Date d1;
	return 0;
}

         初始化列表在构造函数处,以冒号开始,每个成员变量的后面跟上一个括号,括号内是一个表达式或者数字。

        我们可以认为初始化列表是成员变量定义的地方,即使成员变量没有出现在初始化列表中,我们也要经过初始化列表这样过程。

        如果我们在构造函数的内部对成员变量赋上另外的值之后,那刚开始初始化的值就会被改变,这是因为初始化列表仅仅只是赋上初值,对象的值还是可以改变的。

        简单地介绍完初始化列表后,我们自然要讲一下它值得注意的一些小点,首先来看一下下面这道题,为了方便,我们直接把成员变量放在public。

class Date
{
public:
    Date()
        :_month(1)
        ,_year(_month)
    {
        
    }
	int _year;
	int _month;
	int _day;
};

int main()
{

   Date d1;
   cout<<d1._month<<"  "<<d1._year<<endl;//输出的结果是什么?
   return 0;
}

很多人会觉得答案很显然是1 1,但并不是,答案是1 和随机值。

         原因是什么呢?其实是初始化列表的顺序搞的鬼!

初始化列表的初始化顺序与其声明的顺序有关!

        我们可以看出来_year是首先被声明的,所以它在初始化列表中也首先被初始化,但此时_month还是一个随机值,所以说_year也被初始化为随机值,但_month仍然是正常的初始化。 

        为了保证自定义类型的成员能够很好的被初始化,作者的建议是最好都写初始化列表,但并不是绝对的,有时候在成员里有类似数组的成员时,往往在函数体内的构造比较好用!因为在初始化列表的小括号中写比较繁琐而且可读性并不是很好的。 

        除了无默认构造函数的自定义成员,对于const修饰的成员,以及引用成员都需要在初始化列表中初始化,因为后面的两种都需要在声明的时候初始化。

        为了引入下面要讲的内容,我们先来看一下这个代码是否是正确的?

二、explicit关键字 

class Date
{
public:
    Date(int year)
    {
        _year = year;
    }

    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(1);
    Date d2 = 2;
    return 0;
}

d1的初始化显然是正确的,那d2呢?左侧的类型是Date,而右侧是int类型,这显然会产生一个隐式的类型转化。

在这里,作者需要介绍一下关于隐式类型转化的原理,实际上隐式类型转化是用右侧的值拷贝一个同类型的,然后用这个拷贝转换为左侧类型,它并不会对右侧的数据做任何修改,而是对拷贝对象的修改。上述d2处的转换是可以的,这不仅是由于隐式转换的可行性,另一个原因是其构造函数是单参数的,如果是多参数,那可就不行了哦!

那么这种的情况是否可以呢?这次是的类型是Date&的。 

class Date
{
public:
    Date(int year)
    {
        _year = year;
    }

    int _year;
    int _month;
    int _day;
};

int main()
{
    Date& d2 = 2;
    return 0;
}

 答案是否定的,原因是拷贝的对象是具有常属性的,C++是不支持对常量进行引用的。除非加上const修饰。

 既然已经讲到隐式类型转换了,那么如何避免这种隐式类型的转换呢?C++引入了explicit关键字,将构造函数进行修饰,就可以防止这种情况。

 三、static成员

 咱们来看一下这么一道题。

初看其实挺懵逼的,什么都不能用,几乎将能用的计算方法都规避掉了。这里提供一种十分妙的方法,然后作者将会改进这种方法,从而引入我们新的知识点。

我们可以对象每次创建都会调用默认构造函数的特点,定义一个Sum类,实现一个默认构造函数,里面实现加法。再构造一个具有n个元素为Sum对象的数组,这样数组初始化的时候,会对每一个Sum对象进行初始化,就会调用n次的构造函数,这就实现了对于循环的控制。

int sum = 0;
int i = 1;
class Sum
{
public:
    Sum()
    {
        sum += i;
        i++;
    }
};
class Solution {
public:
    int Sum_Solution(int n) {
        Sum a[n];
        return sum;
    }
};

         但是对于全局的变量,往往不是很安全,我们想的是能不能拥有一个成员变量为Sum类所用呢?并且它具有全局变量的静态属性。

        为了解决这种问题,C++引入了static修饰的成员。static修饰的成员具有静态属性,它就和普通意义上的静态变量没什么区别,只是它也受类的访问限定符限制。

        static修饰的成员变量是为所有的对象共有的,换句话说是一模一样的。

有一点要注意,static修饰的成员变量必须在类外进行初始化。

有了static,我们就可以对上面的代码进行改进了。

class Sum
{
public:
    Sum()
    {
        _sum += _i;
        _i++;
    }
    int GetSum()
    {
        return _sum;
    }
private:
    static int _sum;
    static int _i;
};
int Sum::_sum = 0;
int Sum::_i = 1;
class Solution {
public:
    int Sum_Solution(int n) {
        Sum a[n];
        return a[0].GetSum();
    }
};

 为了得到最后的结果,我们可以将静态成员放在public中,也可以实现一个GetSum函数来得到。

当然,Static不仅可以修饰变量,也可以修饰成员函数,被Static修饰的成员函数不具有隐藏的this指针

所以说,静态的成员函数是不能访问非静态的成员变量的,也不能访问非静态的成员函数。 

 

由static修饰的函数在类外,只需要加上类域限定符::就可以访问。所以代码还可以写成这样。

class Sum
{
public:
    Sum()
    {
        _sum += _i;
        _i++;
    }
    static int GetSum()
    {
        return _sum;
    }
private:
    static int _sum;
    static int _i;
};
int Sum::_sum = 0;
int Sum::_i = 1;
class Solution {
public:
    int Sum_Solution(int n) {
        Sum a[n];
        return Sum::GetSum();
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值