类和对象收尾

1.再探构造函数

  • 我们之前实现构造函数,初始化成员变量主要使用函数体内赋值,构造函数还有另一种方式——初始化列表,使用方式::开始 ,分隔成员列表 每个成员变量后跟一个()存放初始值或者表达式
class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		// ...
	}
private:
	int _year;
	int _month;
	int _day;
};
  • 每个成员变量在初始化列表只能出现一次,初始化列表的本质是:每个成员变量定义初始化的地方
  • const成员变量、引用成员变量、没有默认构造的成员变量,必须放在初始化列表位置初始化,否则会报错
// 初始化列表
/* const成员变量、引用成员变量、没有默认构造的成员变量,
   必须放在初始化列表位置初始化,否则会报错
*/
class Time
{
public:
	// 非默认构造
	Time(int hour)
		:_hour(hour)
	{
		cout << "调用Time的构造" << endl;
	}
private:
	int _hour;
};
class Date
{
public:
	Date(int year, int month, int day, int& x,int num)
		:_year(year)
		, _month(month)
		, _day(day)
		// ,_t(6) // 未在初始化列表初始化
		// ,_ref(x) // 未在初始化列表初始化
		// ,_n(num) // 未在初始化列表初始化
	{		
		// error C2512: “Time”: 没有合适的默认构造函数可用
		_t = 6;
		// error C2530: “Date::_ref”: 必须初始化引用
		ref = x;
		// error C2789: “Date::_n”: 必须初始化常量限定类型的对象
		_n = num;
	}

		void Print()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t; // 没有默认构造
	int& _ref;
	const int _n;

};

int main()
{
	int i = 0;
	Date d1(2025,8,14,i,100);
	d1.Print();

	return 0;
}

const成员变量和引用成员变量只有一次初始化的机会就是在定义的时候,在初始化列表中,自定义类型成员会调用它的默认构造,而上述演示Time没有默认构造,所以报错了

以下是修正版本代码:

// 修正版本:
class Time
{
public:
	// 非默认构造
	Time(int hour)
		:_hour(hour)
	{
		cout << "调用Time的构造" << endl;
	}
private:
	int _hour;
};
class Date
{
public:
	Date(int year, int month, int day, int& x, int num)
		:_year(year)
		, _month(month)
		, _day(day)
		 ,_t(6) 
		 ,_ref(x) 
		 ,_n(num) 
	{
		// ...
	}

	void Print()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	Time _t; // 没有默认构造
	int& _ref;
	const int _n;

};

int main()
{
	int i = 0;
	Date d1(2025, 8, 14, i, 100);
	d1.Print();

	return 0;
}
  • C++11支持成员变量在声明的位置给缺省值,这个缺省值是给没有显式在初始化列表的成员使用的
// 在声明时给的缺省值是给初始化列表成员初始化使用
class Time
{
public:
	// 非默认构造
	Time(int hour)
		:_hour(hour)
	{
		cout << "调用Time的构造" << endl;
	}
private:
	int _hour;
};
class Date
{
public:
	Date(int year, int month, int day, int& x, int num)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		// ...
	}

	void Print()const
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
	// C++11 给缺省值用于参数列表成员初始化 此处仅声明 未定义
	Time _t = 6; 
	const int _n = 100;
	const int& _ref = _n;

};

int main()
{
	int i = 0;
	Date d1(2025, 8, 14, i, 100);
	d1.Print();

	return 0;
}

在这里插入图片描述

  • 建议一定要使用初始化列表初始化,那些不在初始化列表初始化的成员也会先走一遍初始化列表,如果这个成员给了缺省值,初始化列表会使用缺省值初始化,如果没有缺省值,对于没有出现在初始化列表的内置成员,是否初始化取决于编译器,对于没有出现在初始化列表的自定义类型成员,会调用这个成员类型的默认构造函数,没有默认构造函数会报错
class Time
{
public:
    // 有默认构造
    Time(int hour = 6)
        :_hour(hour)
    {
        cout << "调用Time的构造" << endl;
    }
private:
    int _hour;
};

class Show
{
public:
    // 没有初始化列表的构造函数
    Show(int num2, char ch2, Time t2)
    {
        // 这里只是赋值,不是初始化
        _num = num2;
        _ch = ch2;
        // _t = t2;  // 即使这样写也会先尝试默认构造_t
    }
    void Print()const
    {
        cout << _num << "   " << _ch << endl;
    }
private:
    int _num;              // 内置类型,无缺省值
    char _ch = 'a';        // 内置类型,有缺省值
    Time _t;               // 自定义类型,无默认构造,无缺省值
    // Time _t2 = Time(10); // 如果这样给缺省值,也可以通过编译
};

int main()
{
    Time t(10);
    Show s(1, 'b', t);  
    return 0;
}

在这里插入图片描述

如果将Time的构造改成非默认构造就会报错

在这里插入图片描述

  • 初始化列表初始化的顺序和成员变量出现的顺序一致

分析下面的代码输出什么?

class A
{
public:
	A(int a)
		:_a1(a)
		, _a2(_a1)
	{

	}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a2 = 2;
	int _a1 = 2;
};
int main()
{
	A aa(1);
	aa.Print();
	return 0;
}

分析:

创建A类变量aa,并传参1给构造函数,按照成员变量声明顺序,先初始化_a2,为随机值,后初始化_a1为传递的参数1

所以最后输出:1 随机值

总结一下初始化列表:

在这里插入图片描述

我们记住核心逻辑即可:

构造函数的初始化列表用于初始化,函数体用于赋值

2. 类型转换

  • C++支持内置类型隐式转换为类类型对象,需要有相关内置类型为参数的构造函数
  • 构造函数前加explicit关键字就不再支持类型转换
  • 类类型对象之间也可以转换,需要相应构造函数支持
// 类型转换
class ShowA
{
public:
	// 单参数构造
	ShowA(int aa)
		:_a(aa)
	{
		cout << "调用构造ShowA(int aa)" << endl;
	}
	// 多参数构造
	ShowA(int aa, int aaa)
		:_a(aa)
		, _aa(aaa)
	{
		cout << "调用构造ShowA(int aa, int aaa)" << endl;
	}
	void Print()
	{
		cout << _a << "   " << _aa << endl;
	}
	int GetA() const
	{
		return _a + _aa;
	}
private:
	int _a;
	int _aa;
};
class ShowB
{
public:
	ShowB(const ShowA& a)
		:_b(a.GetA())
	{
		// ...
	}
private:
	int _b;
};
int main()
{
	ShowA aaa1(10);
	aaa1.Print();
	// 拷贝构造
	ShowA aaa2 = aaa1;

	// 3(int)隐式类型转换成ShowA类 构造一个3的临时对象,再用临时对象拷贝构造aaa3
	// 编译器遇到连续的构造+拷贝构造 会优化成->直接构造
	ShowA aaa3 = 3;
	aaa3.Print();
	// C++11支持多参数转化
	ShowA aaa4 = { 1,2 };
	aaa4.Print();

	ShowB bbb1 = aaa4;
	// 引用的是aaa1构造的临时对象
	const ShowB& rb =aaa1;

	return 0;
}

3. static成员

  • static修饰的静态成员变量必须在类外初始化
  • 静态成员变量为所有类对象共享,存放在静态区
class A
{
public:
	
private:
	int _a = 1;
	float _fa = 2.1;
	// 静态成员变量在类内声明
	static int _aa;
};
// 静态成员变量在类外初始化
int A::_aa = 100;

class B
{
public:
private:
	int _b = 0;
};

int main()
{
	cout << sizeof(A); // 8

	return 0;
}


  • static修饰的静态成员函数没有this指针
  • 静态成员函数可以访问其他静态成员,但不能访问非静态的成员,因为没有this指针
  • 非静态的成员函数可以访问任意的静态成员变量和静态成员函数
  • 静态成员也是类的成员,也受public、protected、private访问限定符的限制

实现一个类,计算程序出现多少个类对象

class Calculate
{
public:
	Calculate()
	{
		++_count;
	}

	Calculate(const Calculate& copy)
	{
		++_count;
	}

	~Calculate()
	{
		--_count;
	}
	static int GetCount()
	{
		return _count;
	}
private:
	// 类内声明
	static int _count;
};
// 类外初始化
int Calculate::_count = 0;

int main()
{
	cout << Calculate::GetCount() << endl;
	Calculate a1, a2;
	Calculate a3(a1);
	cout << Calculate::GetCount() << endl;
	cout << a1.GetCount() << endl;
	//  error C2248: “Calculate::_count”: 无法访问 private 成员(在“Calculate”类中声明)
	// cout << Calculate::_count << endl;
	return 0;
}

求1+2+3+…+n_牛客题霸_牛客网

class Sum
{
public:
    Sum()
    {
        _sum += _num;
        ++_num;
    }
    static int GetSum()
    {
        return _sum;
    }

private:
    static int _num;
    static int _sum;
};
int  Sum:: _num = 1;
int  Sum::_sum = 0;  


class Solution {
public:
    int Sum_Solution(int n) {
        Sum arr[n];
        return Sum::GetSum();  
    }
};

思路:创建一个Sum类的边长数组,在类中定义两个静态变量_num _sum,每调用一次构造函数_sum就加_num,随后_Num加一,数组在完成n个元素初始化的过程中就完成了计算

4. 友元

  • 在函数声明或者类声明的前面加friend,并把友元声明放到一个类中
  • 友元函数仅仅是一种声明,并不是类的成员函数
  • 友元的关系是单向的,AB的友元,但是B不是A的友元,同样友元没有传递性,AB的友元,BC的友元,但A不是C的友元
  • 友元会增加耦合度,不建议多用

5. 内部类

  • 一个类定义在另一个类的内部,这个在类内部的类就称为内部类,内部类是独立的类,仅受类域限制,所以外部类定义的对象中不包含内部类

  • 内部类默认是外部类的友元

6. 匿名对象

  • 类型(实参)定义出来的对象叫做匿名对象
  • 匿名对象的生命周期只在当前这一行,即用即销毁
// 匿名对象
class A
{
public:
	A(int a)
		:_a(a)
	{
		// ...
	}
private:
	int _a;
};
int main()
{
	A a1(10); // 有名对象
	A(3);  // 匿名对象 生命周期仅在这样一行

	return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值