有些情况下,我们创建变量只是临时用一用,以后不会再用到了。这时,如果创建变量还要起名字,是不是很多余?
对此,C++引入了 匿名对象 供临时使用。
例如:如果我们创建对象slt,仅仅是为了调用一次Sum_Solution(),那不如用匿名对象。
匿名对象:指在没有被命名的情况下创建的临时对象。
格式:类名( )
说明:
1.匿名对象的生命周期只在本行。也就是说,一旦语句执行完,会调用匿名对象的析构函数,销毁它。
不信我们来验证下,看看是不是执行完立马销毁:
class Date
{
public:
Date(int year)
:_year(year)
{
cout << "Date()" << endl;
}
~Date()
{
cout << "~Date()" << endl;
}
private:
int _year;
};
int main()
{
Date (2000);
Date d1(1000);
Date d2(1000);
Date d3(1000);
return 0;
}
结果:
补:
创建匿名对象后,编译器可能会报警告,这是正常的。
作用:explicit修饰构造函数,可以禁止类型转换。
在进一步讲解explicit之前,我们先来回顾一下:什么是类型转换?
想一想,Date d1=2022,我们是不是把一个int类型的值赋给一个Date类型的对象?
能够赋值成功,是因为其间默默进行了类型的转换。
细究来说,这两种创建对象的方式是不一样的:
Date d1(2022); //直接调用构造函数
Date d2 = 2022; //本质是一步(隐式)类型转换:2022从int类型转成Date类型
第二种方式可以视为3步:先构造,再拷贝构造,然后编译器优化,最终结果才是调用了构造函数。
2022先构造出一个临时对象(Date类型的2022),再拷贝给d2,此过程会被优化,以提高效率。
如果不想让下面的隐式类型转换发生,那就用explicit修饰构造函数。
class Date
{
public:
explicit Date(int year)
{
_year = year;
}
private:
int _year;
};
int main()
{
Date d1(1000); //d1不受影响
Date d2 = 1000; //d2因涉及类型转换,初始化失败
return 0;
}
无法转换:
如果我想要知道A类型的对象创建了多少个,要怎么办呢?
其实可以创建一个全局变量count来计数,但是,这种方式的缺点在于:
因为是全局的,人人都可以访问。
我们想要的count是:与A捆绑起来,别人访问不了,这个count就专门为A计数。
对于这个问题,在学习了static静态成员之后,我们就知道要怎么处理了。
声明为static的类成员称为类的静态成员:
用static修饰的成员变量,称之为静态成员变量;
用static修饰的成员函数,称之为静态成员函数。
注:静态成员变量一定要在类外 进行 定义/ 初始化。
像这样:
class A
{
private:
static int count; //类里声明
};
int A::count = 0; //类外初始化
了解:为什么静态成员一定要在类外初始化?
因为静态成员属于整个类,它被所有的对象所共享。
如果放在类里面初始化,当有多个对象存在时,每个对象里都含一份该静态成员,造成矛盾。
1.静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
class Date
{
private:
static int _year; //_year在静态区
int _month; //_month在对象里面。若对象在栈区,它就在栈区。
};
2.静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明。
3.怎么访问静态成员?
➡️如果它是公有的:可用 类名::静态成员 或者 对象.静态成员 来访问。
class A
{
public:
A (){
count++;
}
static int count;
};
int A::count = 0;
int main()
{
A a1;
cout << A::count << endl; //用类域来访问
cout << a1.count << endl; //用对象来访问。这里不是说在a1里找,只是帮助突破类域去找
return 0;
}
➡️如果是私有的:通过公有的函数来访问。
class A
{
public:
A(){
count++;
}
int GetCount() {
return count; //可以返回count的值
}
private:
static int count;
};
int A::count = 0;
int main()
{
A a1;
cout << a1.GetCount() << endl; //通过GetCount()来访问count
return 0;
}
4.静态成员函数没有隐藏的this指针,仅能访问静态的成员变量,不能访问任何非静态成员。
5.静态成员也是类的成员,受public、protected、private 访问限定符的限制。
类的私有成员,一般情况下,外界是无法访问的。但实际上,private / protected并非是一堵密不透风的墙,
它对外开了个小口子,我们可以通过小口子访问私有成员。这个小口子就是友元。
友元是C++中的一种关系:像朋友一样亲密到可以访问私人空间的关系。发生在函数和类之间 or 类和类之间。
通过友元的关系,可以让一个函数or类 访问另一个类中的私有成员(private / protected)。
关键字:friend
分类:友元函数 友元类
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,
不属于任何类,但需要在类的内部声明。
声明格式:friend 类型 函数名 (参数);
例:
class A
{
public:
A() {
a = 10;
}
private:
int a;
friend void Print(const A& obj); //友元的声明
};
void Print(const A& obj) {
printf("%d", obj.a); //访问了私有成员a
}
int main()
{
A a1;
Print(a1);
return 0;
}
结果:
说明:
1.友元函数可访问类的私有和保护成员,但不是类的成员函数。
2.友元函数不能用const修饰。
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
4.一个函数可以是多个类的友元函数。
5.友元函数的调用与普通函数的调用原理相同。
我们不仅可以将一个函数声明为一个类的朋友,也可以将一个类声明为另一个类的朋友。
这就是友元类。
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
声明格式:friend class 类名;
示例:
class A; //注意:Print之前要声明一下A!
class Print
{
public:
void print(const A& obj); //如果前面没有声明A,这里会不知道A是什么,导致报错
};
class A
{
public:
A() {
a = 10;
}
private:
int a;
friend class Print;
};
//类外定义print
void Print::print(const A& obj) {
printf("%d", obj.a);
}
int main()
{
A a1;
Print p;
p.print(a1);
return 0;
}
结果:
说明:
1.友元关系是单向的,不具有交换性。
声明A是B的友元,不代表B也是A的友元。
2.友元关系不能传递。
如果C是B的友元, B是A的友元,则不能说明C是A的友元。
3.友元关系不能继承,这个在继承那边再详说。
最后,友元能少用就少用!
虽说友元有助于数据的共享,但它也有副作用:打破了封装,会破坏信息的隐蔽性。
(了解)
概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
如:
class Outside
{
private:
int a;
class Inside
{
private:
int b;
};
};
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。
外部类对内部类没有任何优越的访问权限。
说明:
1.内部类可以定义在外部类的public、protected、private都是可以的。
2.注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3.内部类天生就是外部类的友元。但是外部类不是内部类的友元。
class Outside
{
private:
static int a;
int b;
class Inside
{
public:
void Print(const Outside& obj) {
cout << a << endl; //√,可以直接访问外部类中的static成员
cout << obj.b << endl; //√
}
private:
int b;
};
};
4.sizeof(外部类)=外部类,和内部类没有任何关系。
我们来证明一下:
class Outside
{
private:
int a;
class Inside
{
private:
int b;
};
};
int main()
{
cout << sizeof(Outside);
return 0;
}
结果:
可见,Inside不在Outside里。
实际上,它们是并列的关系,只是你从外部访问Inside,要受到Outside类域的限制。