【C++初阶】类和对象(下)--初始化列表初始化的构造函数、类型转换、静态成员、友元、内部类、匿名对象、拷贝时编译器的优化

目录

一.初始化列表初始化的构造函数

二.类型转换

三.static成员

四.友元

五.内部类

六.匿名对象

七.对象拷贝时编译器的优化


一.初始化列表初始化的构造函数

1.之前给对象初始化是在构造函数体内进行初始化,初始化列表就是以冒号开始,用逗号分割的数据成员列表,在每一个数据后有一个小括号,小括号内可以放初始值或者表达式。

2.每个成员只能在初始化列表中出现一次,可以理解为在初始化列表进行变量的定义。

#include<iostream>
using namespace std;
class Date
{
public:

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

	}
	void print()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2024,7,24);
	d1.print();
	return 0;
}

3.因为const类型的变量,引用只能在定义的时候才能初始化,所以这两个必须进行初始化列表初始化。还有无默认构造函数的自定义类型必须在初始化列表初始化。

4.C++11可以在成员变量声明的时候给缺省值,当该成员变量没有显示写初始化列表时,就会走缺省值。

5.尽量都走初始化列表初始化,因为不管你有没有显示的写出来,每一个成员变量都会走初始化列表初始化,如果在声明的地方给了缺省值,就会用这个缺省值初始化。但是如果没有给缺省值又分两种情况,第一个是内置类型,这个取决于编译器,可能会初始化,也可能给随机值;第二个是自定义类型,如果该类有默认构造函数就走默认构造函数初始化,当没有默认构造时,编译器就会报错。

class Time {
public:
	Time(int hour=1)
		:_hour(hour)
	{

	}
//非默认构造,如果不在Date的初始化列表初始化就会报错
	/*Time(int hour)
		:_hour(hour)
	{

	}*/
private:
	int _hour;
	int _min;
	int _second;
};
class Date
{
public:

	Date(int& xxx, int year, int month, int day)
		:_year(year)
		, _month(month)
		//, _day(day)
		, x(1)
		, xx(xxx)
	{

	}
	void print()
	{
		cout << _year << '/' << _month << '/' << _day << endl;
	}
private:
//这里给缺省值并不是指开了空间,只是未走初始化列表时,给编译器的初始化建议
	int _year=1;
	int _month=1;
	int _day=1;

//const,引用,有无默认构造
	const int x;
	int& xx;
	Time t;
};

6.初始化的顺序是按照声明成员变量的顺序来初始化的。

下代码运行结果是什么?

#include<iostream>
using namespace std;
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();
}

答案是1,随机值。

是因为先声明的_a2,后声明的_a1,所以走初始化列表初始化时,先用_a1初始化_a2,但是此时_a1还并没有被a或1初始化,_a1是随机值,所以_a2被初始化为随机值,_a1被传的参数1初始化为1。

二.类型转换

1.C++支持将内置类型隐式转换为自定义类型,与构造函数参数有关

2.构造函数前⾯加explicit就不再⽀持隐式类型转换

#include<iostream>
using namespace std;
class A
{
public:
	A(int a)
		:_a1(a)
	{}

   /*explicit A(int a)
		:_a1(a)
	{}
    */

	A(int a1,int a2)
		:_a1(a1)
		,_a2(a2)
	{}
	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}
private:
	int _a1 = 1;
	int _a2 = 1;
};
int main()
{
	//单参构造
	//先将2构造一个临时对象,再用临时对象拷贝构造aa
	//构造加拷贝构造会优化为直接构造
	A aa=2;
	aa.Print();
	//多参构造函数
	A aaa = { 3,3 };
	aaa.Print();
}

三.static成员

1.static修饰的成员变量就是静态成员变量,静态成员变量不能在类内初始化,只能在类外初始化。

2.静态成员变量为所有对象共有,不存在变量中,而是存在静态区。

3.static修饰的函数是静态成员函数,静态成员函数中无this指针。

4.静态成员函数,不能够访问非静态成员变量,因为无this指针。

5.非静态成员函数可以访问静态成员变量和静态成员函数。

6.可以突破类域访问静态成员,通过类名::静态成员或者对象.静态成员,来访问静态成员变量和静态成员函数。

7.静态成员同样也受private,public,protect访问限定符的限制。

8.静态成员不能够在声明的地方给缺省值,因为静态成员不存在对象中,不需要走初始化列表初始化。

#include<iostream>
using namespace std;
class A {
public:
	A()
	{
		_second++;
	}
	A(A& a)
	{
		_second++;
	}
	~A()
	{
		_second--;
	}
	//静态成员函数
	static int get_second()
	{
		return _second;
	}
private:
	//静态成员变量
	static int _second;
};
//只能在类外给静态成员变量初始化
int A::_second = 0;
int main()
{
	A a1,a2;
	//类名::成员函数
	cout<<A::get_second() << endl;
	A a3(a1);
	cout << A::get_second() << endl;
	//对象.成员函数
	cout << a1.get_second() << endl;
	//_second是private的,所以不能直接访问需要在公有属性中提供一个接口
	//cout<<A::_second
	return 0;
}

牛客题

class sum{
public:
    sum()
    {
        _sum+=_i;
        _i++;
    }
    static int get_sum()
    {
        return _sum;
    }
private:
    static int _i;
    static int _sum;  
};
int sum::_i=1;
int sum::_sum=0;

class Solution {
public:
    int Sum_Solution(int n) {
    sum x[n];
    return sum::get_sum();    
    }
};

下面代码构造和析构的顺序是什么?

C c; 
int main()
 {
 A a;
 B b;
 static D d;
 return 0
;

先构造的后析构,全局最先创建,局部静态到语句的时候才创建。

四.友元

1.友元提供了一种突破访问限定符的方法,友元分为友元函数和友元类,只需要在函数或类的声明前加一个friend就可以,并且声明放在一个类中。

2.外部的友元函数可以访问内部的私有和保护成员,友元函数并不是类的成员函数。

3.友元函数可以在类的任何地方声明不受限制。

4.一个函数可以是多个类的友元函数。

#include<iostream>
using namespace std;
// 前置声明,都则A的友元函数声明编译器不认识B
class B;
class A
{
	// 友元声明
	friend void func(const A& aa, const B& bb);
private:
	int _a1 = 1;
	int _a2 = 2;
};

class B
{
	// 友元声明
	friend void func(const A& aa, const B& bb);
private:
	int _b1 = 3;
	int _b2 = 4;
};
//一个函数可以是多个类的友元函数
void func(const A& aa, const B& bb)
{
	cout << aa._a1 << endl;
	cout << bb._b1 << endl;
}
int main()
{
	A aa;
	B bb;
	func(aa, bb);
	return 0;
}

5.友元类中的成员函数是另一个类的友元函数,可以访问另一个类的私有和保护成员。B是的友元类,则B中的函数可以访问A的私有和保护成员。

6.类的友元不具有传递性和交换性。

7.友元会提高代码的耦合性,但是会降低封装,所以友元少用。

#include<iostream>
using namespace std;
class A
{
	// 友元声明
	friend class B;
private:
	int _a1 = 1;
	int _a2 = 2;
};
class B
{
public:
	void func1(const A& aa)
	{
		cout << aa._a1 << endl;
		cout << _b1 << endl;
	}
	void func2(const A& aa)
	{
		cout << aa._a2 << endl;
		cout << _b2 << endl;
	}
private:
	int _b1 = 3;
	int _b2 = 4;
};
int main()
{
	A aa;
	B bb;
	bb.func1(aa);
	bb.func2(aa);
	return 0;
}

五.内部类

1.一个类定义在另一个类的类中,就叫内部类。内部类是一个独立的类,内部类只受类域和访问限定符的限制,并不存在于外部类的对象中。

2.内部类是外部类的友元类。

3.内部类也是一种封装,如果A和B的关系密切,A中的方法供B类使用,那就可以将A类定义为B的内部类(A类的成员函数可以访问B类的私有成员),如果放到private或public的位置,那A就是B类的专属友元类。

#include<iostream>
using namespace std;
class A {
public:
	//内部类B,默认为A的友元
	class B {
	public:
		//为A提供方法,可以访问A中的成员变量
		void func1(A&aa)
		{
			cout << aa._a1 << endl;
			cout << _b1 << endl;
		}
		
		void func2(A& aa)
		{
			cout << aa._a2 << endl;
			cout << _b2 << endl;
		}
	private:
		int _b1 = 2;
		int _b2 = 2;
	};
private:
	int _a1=1;
	int _a2 = 1;
};
int main()
{
	//A类大小为8,说明A类的对象中不包含B
	cout << sizeof(A) << endl;
	A a;
	//B类在A的类域,用::,否则编译器报未声明
	A::B b;
	//通过B访问A中的私有成员变量
	b.func1(a);
	b.func2(a);
	return 0;
}

所以1+2+3+4+...+n可以改写成

class Solution {
public:

class sum{
public:
    sum()
    {
        _sum+=_i;
        _i++;
    }

};
    static int _i;
    static int _sum;  
    int Sum_Solution(int n) {
    sum x[n];
    return _sum;   
    }
};
int Solution::_i=1;
int Solution::_sum=0;  

六.匿名对象

1.用类型(实参)定义出来的叫匿名对象,用类型 对象名(实参)实例化的对象叫有名对象。

2.匿名对象的生命周期只在当前那一行,通常只需要临时用一下的时候才使用匿名对象。

#include<iostream>
using namespace std;
class A
{
public:
    A(int a =0)
        :_a(a)
    {
        cout << "A(int a)" << endl;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }
private:
    int _a;
};
int main()
{
    A aa1;
    A(1);
    A aa2;
	return 0;
}

第二三行就是因为创建的是匿名对象,生命周期只有一行,构造之后到下一行就马上进行析构。

七.对象拷贝时编译器的优化

1.现代编译器会为了尽可能提⾼程序的效率,在不影响正确性的情况下会尽可能减少⼀些传参和传参 过程中可以省略的拷⻉。

2.如何优化C++标准并没有严格规定,各个编译器会根据情况⾃⾏处理。当前主流的相对新⼀点的编 译器对于连续⼀个表达式步骤中的连续拷⻉会进⾏合并优化,有些更新更"激进"的编译还会进⾏跨 ⾏跨表达式的合并优化。

#include<iostream>
using namespace std;
class A
{
public:
    A(int a = 1)
        :_a1(a)
    {
        cout << "A(int a)" << endl;
    }
    A(const A& aa)
        :_a1(aa._a1)
    {
        cout << "A(const A& aa)" << endl;
    }
    A& operator=(const A& aa)
    {
        cout << "A& operator=(const A& aa)" << endl;
        if (this != &aa)
        {
            _a1 = aa._a1;
        }
        return *this;
    }
    ~A()
    {
        cout << "~A()" << endl;
    }

//private:
    int _a1;
};
void func1()
{

}
void func3(A aa)
{
    aa._a1 *= 10;
    cout<< aa._a1<<endl;
}
void func2(A& aa)
{
}

int main()
{
    //A aa1;
    //func1(aa1);

    //A aa2;
    //func2(aa2);

    //func1(1);
   // func1(A(2));

    //A aa3;
    //A aa4 = aa3;
    A aa5;
    func3(aa5);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值