类和对象2

目录

一初始化列表

1 初始化列表的基本定义

2 初始化列表的本质

3 初始化列表的顺序

4 必须用初始化列表的类型

①自定义类型的成员变量

②const修饰的成员变量

③引用成员变量

5 初始化列表和函数内部初始化的比较

二 explicit关键字

三 匿名对象

 四 static

1特性:

五 friend 友元

1 分类:

2 友元函数的特性:

3 友元类的特性

六 内部类


一初始化列表

1 初始化列表的基本定义

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

	}
};
int main()
{
	Date d(1, 1, 1);
	return 0;
}

这样就可以实现成员变量year,month和day的初始化了。

2 初始化列表的本质

初始化列表可以被认为是成员变量被定义的地方。先去完成默认构造,再去赋值给对应的变量。

c++中对于内置的类型不会处理,所以内置的类型一般是随机值。c++后来打了一个对应的补丁,那么对于内置类型的缺省值其实相当于在初始化列表中给的值。当你没有指定对应的值,就是1,1,1,;但是当你传入了对应的值,就是你所传入的值了。

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

	}
};

因此如果有必要的话,我们也可以对这些内置类型在初始化列表的时候进行定义。

3 初始化列表的顺序

初始化列表定义的顺序和初始化列表中的顺序无关,取决于声明的时候的顺序。

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

}

有这样一段程序可以说明上述的问题。由于声明是先声明了_a2,再声明了_a1,因此初始化列表也会先去定义_a2,再去定义_a1,因此就与初始化列表中先1后2的顺序没有多大的关系。

这样的一段程序,由于_a2是的定义要依靠_a1,但是此时_a1还没有被定义,是一个随机值,因此_a2也是一个随机值。对于_a1的话,是被a来定义的,因此_a1的值就是a的值,是1.

4 必须用初始化列表的类型

①自定义类型的成员变量


自定义类型的成员变量,编译器会自动对他进行初始化。但是如果对于一个类中有另一个自定义类型的成员变量,但是这个变量他没有默认构造函数,那么编译器也无法对他进行初始化了。比如下面这段程序:

class Time
{
private:
	int hour;
public:
	Time(int _hour)
	{
		hour = _hour;
	}
};
class Date
{
private:
	int year;
	int month;
	int day;
	Time _t;
public:
	Date(int _year, int _month, int _day, int _hour)
	{
		year = _year;
		month = _month;
		day = _day;
		Time t(_hour);
		_t = t;
	}
};

编译器会报错,没有合适的默认构造函数可以使用。

 但是如果使用初始化列表的方式就可以编译通过了,虽然初始化的值默认给的是随机值:

class Time
{
private:
	int hour;
public:
	Time(int _hour)
	{
		hour = _hour;
	}
};
class Date
{
private:
	int year;
	int month;
	int day;
	Time _t;
public:
	Date(int _year, int _month, int _day, int _hour)
		:_t(_hour)
	{
		year = _year;
		month = _month;
		day = _day;
	}
};

但是需要注意的是,如果你默认给了的话,即使在函数体内初始化也是可以编译通过的

class Time
{
private:
	int hour;
public:
	Time(int _hour=10)
	{
		hour = _hour;
	}
};
class Date
{
private:
	int year;
	int month;
	int day;
	Time _t;
public:
	Date(int _year, int _month, int _day, int _hour)
		
	{
		year = _year;
		month = _month;
		day = _day;
		Time t(_hour);
		_t = t;
	}
};

②const修饰的成员变量

由于const修饰的成员变量是在被定义的时候初始化的,她是一个只读类型的变量。(c++11中新引入的一个标准,让他可以直接在声明的时候给值)

③引用成员变量

引用成员变量也是在定义的时候就被初始化了的,因此也是必须在初始化列表进行初始化。

5 初始化列表和函数内部初始化的比较

对于自定义类型,如果在函数体内初始化的话,需要先拷贝构造形成一个临时变量t,再拷贝构造对_t进行初始化。那倒不如直接在初始化列表进行初始化。

还有一些内置类型她的本质上也是走的初始化列表来进行初始化,那也可以在初始化列表进行初始化,虽然这种类型在函数体内和初始化列表没什么差别。

还有一些必须是在初始化列表初始化的数据类型。

所以总结就是,能用初始化列表进行初始化,最好就用初始化列表进行初始化。

但是对于一些特殊的,比如要去malloc一段空间来开辟数组的这种,直接在初始化列表初始化有点牵强了,可以在函数体内初始化。

它们二者各有对应的场景。

二 explicit关键字

讲这个关键字之前,我们先了解一下:对于一个类来说,如果只有一个成员函数,并且有一个单参数的构造函数,那么如果直接使用“=”,编译器会自动进行隐式类型的转换。

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

int main()
{
	Date d(1);//构造函数
	Date d1 = 1;//构造函数+拷贝构造函数-》(优化之后)直接调用构造函数
	return 0;
}

explicit这个关键字,就是阻止这个隐式类型的转换发生的 

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

int main()
{
	Date d(1);//构造函数
	Date d1 = 1;//
	return 0;
}

加上这个关键字上述操作就无法发生了。

由于存在上述这个隐式类型的转换,因此string类中可以直接这样实现初始化:

string s = "hello world";

三 匿名对象

生命周期只有对应的这一行。有时候可能只需要在这一行内用到,就会出现对应的匿名对象。

Date(1);

可以直接这样指定。

 四 static

1特性:

①静态成员(变量或者函数)存放在静态区,她的生命周期在整个程序的运行期间。为所有的类对象共享。

②在类外定义,无法直接在声明的时候给值,也无法在初始化列表进行定义。类内只是声明。

class Date
{
private:
	int year;
	static int month;
public:
	Date(int _year)
	{
		year = _year;
	}
	
};
int Date::month = 1;

③可以用::或者.来访问

此时是public的,才能被访问到

cout << Date::month;
	cout << d.month;

④他仍然会受到类访问作用限定符的限制。因此对于private这类的成员变量,可以使用static的函数return回对应的值。                  

class Date
{
private:
	int year;
	static int month;
	
public:
	Date(int _year)
		
	{
		year = _year;
	}
	static int Getmonth()
	{
		return month;
	}
};
int Date::month = 1;
int main()
{
	Date d(1);//构造函数
	cout << Date::Getmonth();
	return 0;
}

  ⑤由于他不是属于具体的某个对象的,他是属于整个类的,他没有this指针。(this指针指向当前的对象,只有非静态成员函数才有)

因此,静态成员函数他不能调用非静态的成员函数。

但是非静态成员函数可以调用静态的成员函数,因为非静态成员函数有this指针,他可以调用对应的成员函数。

五 friend 友元

1 分类:

友元函数和友元类

2 友元函数的特性:

①不是类的成员函数,他是在类外定义的一个普通函数,但是在类内声明了是友元的话,就可以访问类的私有和保护成员了。

class Date
{
private:
	int year;
	friend void show(Date d);
public:
	Date(int _year=1)
		
	{
		year = _year;
	}
	
};

 void show(Date d)
{
	 printf("%d", d.year);
}
int main()
{
	Date d(2011);//构造函数
	show(d);
	return 0;
}

②不能用const修饰。因为他不是成员函数。

friend void show(Date d) const;

但是如果这个函数本身就是const可以 

class Date
{
private:
	int year;
	friend void  const show(Date d);
public:
	Date(int _year=1)
		
	{
		year = _year;
	}
	
};
void const show(Date d)
{
	 printf("%d", d.year);
}

③由于是一个类外的普通函数,因此不受类访问限定符的限制,并且可以在类内的任何地方进行声明。

上述例子就是在private中声明,但是最后也能访问。

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

class Time
{
private:
	int hour;
	friend void show();
public:
	Time(int _hour=10)
	{
		hour = _hour;
	}
	int setTime(int b)
	{
		hour = b;
		return hour;
	}
};

class Date
{
private:
	int year;
	friend void show();
public:
	Date(int _year=1)
		
	{
		year = _year;
	}
	int setDate(int a)
	{
		year = a;
		return year;
	}
	
};
void  show()
{
	Date d;
	Time t;
	int ret=d.setDate(10)+t.setTime(10);
	cout << ret;

}

3 友元类的特性

①友元类是另一个类的友元函数,可以访问另一个类的私有和保护成员。

注意:友元函数是单向的,不具有交换性质。并且不能被继承,也不能传递。

六 内部类

 把一个类定义在另一个类的内部,叫做内部类。

class Date
{
private:
	int year;
	
public:
	Date(int _year=1)
		
	{
		year = _year;
	}
	class Time
	{
	private:
		int hour;

	public:
		Time(int _hour = 10)
		{
			hour = _hour;
		}

	};
};
int main()
{
	cout << sizeof(Date);
}

Date的大小与Time无关。 

因为这只是声明而非定义,除非用Time再实例化一个对象出来,才有关系。

性质:

内部类受到外部的类域的限制,也会受到访问限定符的限制。

并且内部类天生是外部类的友元,也就是说内部类可以访问外部类的私有变量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值