类和对象(5)--《Hello C++ Wrold!》(7)--(C/C++)--构造函数的初始化列表,explicit关键词,友元,内部类和匿名对象

构造函数的初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

(初始化列表是构造函数的一部分)

添加位置:在构造函数的{}前面

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

只要调用了构造函数,都会走一次初始化列表,没有的话,就走隐式初始化列表

隐式初始化列表的规则:

对于内置类型,高级的编译器会初始化其为默认值(比如int的搞成0)
对于类类型(即对象成员):
如果该类型有默认构造函数,则会调用默认构造函数;如果没有默认构造函数且未显式初始化,则编译错误。

注意:每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

类中包含以下成员,必须放在初始化列表位置进行初始化:(成员是private时)

1.引用成员变量

2.const成员变量

(因为引用成员变量和const成员变量的特征是必须在定义时初始化)

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();
}
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值//说成随机值也行,但是一般都是0
//在调用构造函数的时候,肯定已经定义对象了,所以已经给_a1分配内存了,不单单只有声明了
解:应该选D

explicit关键字

A aa1(1)和A aa2 = 1还是有一点区别的:
A aa2 =1 是隐式类型转换,整形转换成自定义类型
也就是1构造一个A的临时对象,临时对象再拷贝构造aa2,但是许多编译器都会用2直接构造

注意点:接受单个参数的构造函数才能这么搞:A aa2 =1

接收单个参数的构造函数具体表现:
1. 构造函数只有一个参数
2. 构造函数有多个参数,除第一个参数没有默认值外,其余参数都有默认值
3. 全缺省构造函数

explicit修饰构造函数,用来禁止类型转换

使用举例:
explicit Date(int year)
 :_year(year)
{}

static成员

概念:

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。

注意:静态成员变量一定要在类外进行初始化

应用:弥补了全局变量在任意地方都能被改变的缺陷(static成员变量可以替代他)

特性:

1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区,程序结束才调用他的析构

(是所有类对象公有的,一个对象的这个静态变量改变,其他对象的这个变量也会随之改变)

2.静态成员变量必须在类外定义(初始化列表也不走),定义时不添加static关键字,类中只是声明

(但是有些时候又不会报错(不建议声明和定义不分离哈),比如:const static size_t npos = -1)

static成员就算是私有的,在类外也可以定义
用法: 声明时用: static int a;
类外定义:eg: int A::a = 1;

3.类静态成员只可用 类名::静态成员 或者 对象.静态成员 来访问

(不要跟普通的成员函数和成员变量搞混了,这两个只能用对象.成员来访问)

4.静态成员函数没有隐藏的this指针,不能访问任何非静态成员

5.静态成员也是类的成员,受public、protected、private 访问限定符的限制

非静态成员能否调用静态成员:可以

静态成员能否调用非静态成员不可以

原因:因为没有this指针,但是非静态成员(包括函数)的调用需要this指针

因此: 静态成员函数和静态成员变量一般是配套出现的

友元

友元分为友元函数和友元类,但是友元一般不推荐用,会破坏封装性

友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

friend istream& operator>>(istream& _cin, Date& d);
//也就是在普通函数声明前面加个friend
istream& operator>>(istream& _cin, Date& d)
{
 _cin >> d._year;
 return _cin;
}

性质:

1.友元函数可以访问类里面的所有成员,但是友元函数不是类的成员函数

2.友元函数不能用const修饰,但是其参数可以用const修饰

(const修饰函数的情况一般是成员函数(因为有this指针))

3.友元函数可以在类定义的任何地方声明,不受类访问限定符的限制

(比如在private那声明和在public处声明一样)

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

5.友元函数的用法和普通函数的用法相同

本质:友元函数本身并不是类的成员函数,而是被类授予了访问其私有(private)和保护(protected)成员权限的外部函数

友元类

友元类(A)就是在另一个类(B)里面声明A是B的友元

友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的所有成员。

举例:friend class Date;

注意点:

1.友元关系是单向的,不具有交换性

2.友元关系不能传递

举例:B是A的友元,C是B的友元,但是不能说C是A的友元

3.友元关系不能继承

内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。

注意:

内部类是一个独立的类,它不属于外部类,只是在外部类里面声明了一个类型而已

内部类是外部类的天生友元,但是外部类不是内部类的天生友元

这个内部类的话,C++也很少用

特性:

1.内部类不会占用外部类的存储空间,sizeof(外部类)=外部类的,和内部类没有任何关系

2.内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名(访问其他成员的话要对象)(友元类和函数的话是需要的)

class A
{
private:
 static int k;
 int a;
public:

 class B //B是A的内部类
 {
 void foo(const A& a)
 {
 cout << k << endl;
 cout << a.h << endl;
 }
 };

};

int A::k = 1;

内部类的实例化:A::B bb;

3.内部类可以定义在外部类的public、protected、private都是可以的

注意:内部类和外部类都需要使用的成员的话,一般搞在外部类

匿名对象

概念:没有名字的对象

匿名对象也具有常性

举例:
class Solution {
public:
static test(const Solution& b)
{}
   Solution(int b)
{
_a = b;
}
    int Sum_Solution(int n) {}
private:    int _a;
};

int main()
{
 Solution aa(1);//有名对象
 Solution(1);//匿名对象

匿名对象的特殊使用:
//Solution的()不可以省
//(无需传参的话也要这个,需要传参的话,就跟有名对象搞法一样去传)
Solution().Sum_Solution(20);
 return 0;
}

注意:

有名对象的生命周期在当前函数的局部域

匿名对象的生命周期在当前行,但是匿名对象有续命方法:

const引用可以延长匿名对象的生命周期,生命周期在当前函数的局部域

比如:const Solution& ra = Solution(1);

Solution::test(Solution(1));
//如果test不加const和&的话,匿名对象的生命周期就到不了函数里面去
//如果都去了的话,也行,就算拷贝了(这个易忘);单有const或&不行

编译器对连续构造的优化

在一个表达式里(短的那种)连续构造,编译器基本上都会将其优化成一次构造;
多个对象时,只会尽量优化,不一定能优化成一次
(构造和拷贝构造都算构造,拷贝函数是构造函数的一种)

当然,这里到底是合几为一要看编译器的做法,

我们要做的是:构造和拷贝构造能合起来搞就合起来搞

比如:假设A是一个类
void Func1(A aa){}

A aa1(1); Func1(aa1);//这样编译器就优化不了
Func1(A(1));//这样编译器就会去优化->本为构造+拷贝构造

Func1(1)和A aa1 =1;的过程差不多

作业部分:

设计一个类,在类外面只能在栈上创建对象:

class A
{
public:
	static A GetStackObj()
	{
		A aa1;
		return aa1;//这样来限制了只能构造对象在栈里面,每次调用生成的对象不一样
	}
private:
	A()//把构造函数搞成私密
	{}
};

引申:(在类外定义对象)
static A a1;   // 存在静态区
	A a2;          // 存在栈 
	A* ptr = new A; // 存在堆
牛客 JZ64 求1+2+3+...+n
要求不能使用不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句
这边外加要求不能使用递归和位运算

解法:创建一个类型是类的数组(大小为n),类的构造函数里面ret+=i;i++
ret和i都搞成的是静态成员变量(初始值都是0)
注意获取ret的成员函数要搞成static,因为静态成员调用不了非静态的

牛客 JZ64 求1+2+3+…+n
在这里插入图片描述

在一个cpp文件里面,定义了一个static类型的全局变量,下面一个正确的描述是:(A)
A.只能在该cpp所在的编译模块中使用该变量
B.该变量的值是不可改变的
C.该变量不能在类的成员函数中引用
D.这种变量只能是基本类型(如int,char)不能是C++类型

A的引申:
局部变量中的 static 会修改局部变量的生命周期;
局部变量,全局变量和函数前的 static,只能在当前.cpp文件中使用
即使其他文件链接了,也找不到,因为static修饰之后不进入符号表

在这里插入图片描述

在这里插入图片描述

评论 69
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值