类和对象——初始化列表

目录

初始化列表

注意

单/多参数传入

explicit关键字

匿名函数


先前,我们知道有构造函数——用于给各成员变量一个初始值。

但是仍然不能称为是初始化,因为初始化只能初始化一次,但是构造函数里可以多次赋值

初始化列表

以日期类Date为例,进行一个初始化列表

class Date
{
public:
//初始化列表—每个成员定义的地方
	Date(int year, int month, int day)
		:_year(year) //成员变量会被赋值为括号内相应的值
		, _month(month)
		, _day(day)
        ,_x(1)
    {
        //_day=day;//此处只能写在中括号里,中括号外必须要有' () '去定义    
    }  //中括号内实现其他代码
private:
	int _year;
	int _month;
	int _day;

    const int _x=10;//const修饰的变量必须要在定义的时候初始化-后续不能修改,总之在类里必须有个地方进行赋值初始化
};

注意

1.缺省值是赋予初始化列表的

2.若初始化列表没有显示给值,就用这个缺省值

3.如果显示给值了,就不用这个缺省值(会先赋值缺省值,如果在列表里重新定义,就会重新赋值)

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

单/多参数传入

单参数直接用 ' () '或者用' = '进行拷贝构造,多参数不仅可用' () ' 还能用' {} '

//单参数
class A
{
public:
	A(int i)
	:_a(i)
	{
		cout << "a(int i)" << endl;
	}
private:
	int _a;
};
//多参数
class B
{
public:
	B(int b1, int b2)
		//explicit B(int b1, int b2)
		:_b1(b1)
		, _b2(b2)
	{
		cout << "B(int b1, int b2)" << endl;
	}
private:
	int _b1;
	int _b2;
};

int main()
{
    const A& ref = 2;
	B bb1 (1, 2);
	B bb2 = { 2,2 };
	const B& ref2 = { 3,3 };//相同空间用引用,需转换类型,加const修饰
    return 0;
}

explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。

class A
{
public:
	//explicit A(int i)//在构造函数这里加上explicit,可以防止隐式转换——这样主函数的两个转换就不能使用了
	A(int i)
	:_a(i)
	{
		cout << "A(int i)" << endl;
	}
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	~A()
	{
		cout << "Delete" << endl;
	}
private:
	int _a;
};

int main()
{
    A aa1(1);
    /单参数构造函数的隐式类型转换
	//用2调用A构造函数,先生成一个临时对象,再用这个对象去拷贝构造aa2
	//编译器会优化,优化用2直接构造
	A aa2 = 2;//结果是直接构造,而不是先对2进行了拷贝构造,再把这个对象赋值给aa2

    //2会进行隐式类型转换,过程中会生成临时对象,具有常性,所以需要const去修饰
	const A& ref = 2;//2转换,生成了一个临时对象具有常性,需要用const来修饰
    
    return 0;
}

用explicit修饰构造函数,将会禁止构造函数的隐式转换。

匿名函数

顾名思义,就没进行命名的对象——生命周期仅存在于定义的那一行

    A aa6(6);//有名——生命周期在当前局部域
	A (7);//匿名——生命周期只在这一行
	//运行后发现的确调用了一次析构,说明出了这一行就会销毁

    //先定义再传入数据,代码量多
	SeqList s;
	s.PushBack(aa6);

	//用匿名对象传,直接放入想要的数据
	s.PushBack(A(8));

除此之外,匿名函数还有其他用法

class Solution {
public:
	int Sum_Solution(int n) {
		// ...
		cout << n << endl;
		return n;
	}
private:
};

int main()
{
    Solution s1;
	s1.Sum_Solution(10);
	Solution().Sum_Solution(100);//这种方法无需定义一个对象,直接赋值,调用类里的函数。
	return 0;

}

Static成员

现在我们知道,一个类里边可以建立很多个对象,那我们是否可以统计出累积创建了多少个对象,并且执行到某一处正在使用的还有多少个对象

引入

可以用以下代码

//全局变量
int n=0;
int m=0;
class A
{
    A()
    {
        ++n;
        ++m;
    }
    A(const  A& t)
    {
        ++n;
        ++m;
    }
    ~A()
    {
        --m;
    }
    void Print()
    {
    cout<

结果:

类和对象——初始化列表_第1张图片

 修正

但是如果将n和m放在全局变量的话,容易被修改,那怎么解决呢?——放入类里,仅需要改变private里的内容

class A
{
public:
    {
        //....
    }
//private:
	//静态成员变量属于所有A对象,属于所有类
	  /*int n=0 ;//创建
	  int m=0 ;//正在使用 */err,因为这样的话,每个对象都会有一个m和n,但我们仅需要一个全局的,所有对象共有的
        
        static int n;
        static int m;
        
}

int A::m=0;
int A::n=0;

访问形式

int main()
{
	A aa1;
	A aa2;
	cout << A::n << " " <

static定义的特点

1.static定义的变量处于静态区里,不参与类大小的计算

sizeof(A);//———— 1

因为不参与类大小的计算,所以内存可视为0,但是类的构建是需要内存的,所以最少会分配一个字节的空间

int main()
{
    A* ptr = nullptr;
	cout << ptr->n << " " << ptr->m << endl;//为什么行得通?
    return 0;
}

因为此处n和m存在于静态区,类似于成员函数的公共代码区,所以一样可以访问。

2.静态成员变量属于所有A对象,属于所有类

3.在类中由于用static定义了——所以属于全局,所以不能给缺省值

4.静态变量不能走初始化列表,不属于对象里,故不能给缺省值,是在静态区里的

5.限制了,不能在外边直接访问并且修改,除非得到m/n的别名,访问方式同其他类' :: '

' . '

static静态成员函数

因为n和m是属于所有A对象,属于所有类,所以需要调用一次类去打印

若用匿名函数

int main()
{
    A();//能否用匿名函数去打印?
    A();
    A::Print();
    return 0;
}

若用匿名函数,那么同样也会调用一次类,那么就会对数据造成误差。

此时就要用到静态成员函数

static void Print()
	{
		//x++; //不能访问非静态,因为没有this指针
		cout << m <<" "<< n << endl;
	}

静态成员函数特点

1.不具有this指针

2.不能访问非静态变量(仅能访问static定义的变量)

倘若想对m和n进行修改呢???——就需要静态引用返回——既不会额外改变m也不会直接改变m

static int& GetM()
	{
		return m;
	}
++A::GetM();

例题

现在我们知道了通过每次的构造函数,可以实行计数

类和对象——初始化列表_第2张图片

 (求1+2+3+...+n_牛客题霸_牛客网)

我们需要建立两个类,一个类用于返回结果,另一个用来统计——怎么统计?——通过每次的构造实现n个有序数字的累加

class Sum
{
public:
    Sum()//构造函数,开创一次就进入一次
    {
        _ret+=_i;
        _i++;
    }
    static int GetRet()
    {
        return _ret;
    }


private:
    static int _ret;//用static修饰,这样可以保证所有对象共用这个_ret
    static int _i;
};

int Sum::_ret=0;
int Sum::_i=1;
class Solution
{
public:
    int Sum_solution(int n)
    {
        Sum a[n]
        return Sum::GetRet();
    }
};

友元函数

对于有private限定符的类,若是某个定义在类外的函数需要调用类内部的数据,那么就需要友元函数

以输出流/输入流为例

class Date
{
	//友元声明——这样可以在外部访问私密对象
	//friend void operator<<(ostream& out, const Date& d);
	friend ostream& operator<<(ostream& out, const Date& d);
	friend istream& operator>>(istream& in, Date& d);
public:
    {
        //..
    }
private:
    {}
}

内部类

特点/定义

1.一个类定义在另一个类的内部

2.内部类是一个独立的类,不受限制,且是外部类的友元

class C
{
public:
	class D
	{
	public:
		void FuncD()
		{
			;
		}
	private:
		int _d;
	};
private:
	int _c;
};

sizeof(C);//  ——4

读取类C大小为4——因为内部类D还是独立的,所以读取的还是C的大小。

3.内部类是外部类的友元,内部类可以读取外部类的变量

class C
{
public:
	class D
	{
	public:
		void FuncD()
		{
			C cc;
			cc._c = 1;//内部类D可以直接访问外部类C的私有成员变量
		}
	private:
		int _d;
	};
	void func()
	{
		D dd;
	}
private:
	int _c;
};

4.public不能去掉,这样的话D类就不属于C类的公共区域里,

int main()
{
    C::D dd1;//但是定义还是得用C类域内部的D类定义
	//若去掉public,那么D类不是公共的,就不能调用
	cc.func();//也不能调用,因为func是属于D类的,但是D类不公共
    return 0;
}

内部类实现循环

再次谈回上边的例题,这次可以通过内部类来实现循环

(求1+2+3+...+n_牛客题霸_牛客网 (nowcoder.com))

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

int Solution::_ret=0;
int Solution::_i=1;

同理,还是需要用到static静态成员,将每次的_ret和_i 进行保存在全局变量里

本质上还是用到了构造函数的访问进行累加

你可能感兴趣的:(开发语言,c++)