在经过前面两篇文章的学习,我们已经对类和对象有了一个比较全面的认识了,接下来让我们学习类与对象的最后一节内容。
一.再探构造函数
在上一篇文章中,我们学习了类内的一些成员函数,现在我们来更深入的学习构造函数。
C++引入了一种新的构造函数初始化方法——初始化列表,在函数后加冒号,然后成员变量间用逗号隔开。
Person(int age,int weight):_age(age),_weight(weight){}
注意
每个成员变量只能在初始化列表初始化一次,我们可以把初始化列表认为是成员变量定义的地方。
class Person
{
public:
int _age;
int _hight;
int _weight;
Person(int age,int hight,int weight):_age(age),_hight(hight),_weight(weight){}
};
引⽤成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进⾏初始
化,否则会编译报错。
class Person
{
public:
int _age;
int _hight;
int _weight;
int& ret;
const int* aa;
Person(int age, int hight, int weight) :
_age(age),
_hight(hight),
_weight(weight),
ret(age),
aa(nullptr)
{}
};
C++11⽀持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显⽰在初始化列表初始化的
成员使⽤的。
class Person
{
public:
int _age=10;
int _hight=70;
int _weight=155;
Person(int age, int hight, int weight) :
_age(age),
_hight(hight)
{}
};
初始化列表的初始化顺序与声明顺序有关,如果一个成员变量的声明在最后一个,即使在初始化列表中它排在第一个位置也不会初始化,而是等待其他成员变量初始化后才初始化它。
这里我们举一个反例来看,输出结果可以证明红字,如果根据初始化列表的顺序来初始化成员变量,那么a和b的值都应该为2才对,但二者的值都为1,所以初始化列表是根据成员变量的声明顺序来初始化成员变量的。
#include <iostream>
using namespace std;
class Person
{
public:
int a = 1;
int b = 1;
Person(){}
Person(int a, int b):b(2),a(b)
{}
void Printf()
{
cout << a << " " << b << endl;
}
};
int main()
{
Person p;
p.Printf();
}
//输出结果为1 1
在使用参数化列表时,如果参数列表内部有自定义类型,需要注意自定义类型是否至少有一个构造函数,否则就会报错
下面为正确的代码,在A类内写了一个有参构造函数。
#include <iostream>
using namespace std;
class A
{
public:
int c;
A(int cc):c(cc){}
};
class Person
{
public:
int a = 1;
int b = 1;
A cc;
Person(int a, int b):a(10), b(10),cc(10)
{}
void Printf()
{
cout << a << " " << b << endl;
}
};
int main()
{
Person p(20, 30);
}
二.隐式类型转换
C++⽀持内置类型隐式类型转换为类类型对象,需要有相关内置类型为参数的构造函数。
class Person
{
public:
int a = 1;
int b = 1;
Person(int aa):a(aa){}//如果没有这个构造函数Person p=10就会报错
Person(int a, int b):a(10), b(10)
{}
void Printf()
{
cout << a << " " << b << endl;
}
};
int main()
{
Person p = 10;
}
构造函数前⾯加explicit就不再⽀持隐式类型转换
三.static成员
静态成员变量为所有类对象所共享,不属于某个具体的对象,不存在对象中,存放在静态区。它不能给缺省值初始化,也不能用初始化列表,它在静态区中,不会走初始化列表。
用static修饰的成员变量,需要在类内声明,类外初始化。
用static修饰的函数叫做静态成员函数,不可以访问非静态成员变量,因为没有this指针,非静态成员函数可以访问静态成员变量和函数。
静态成员和静态成员函数的访问都受类作用域限定符的影响,它们都是类的成员。
class A
{
public:
//static int a=10;不可给缺省值
static int a;
int b;
static void GetA()
{
//b = 10;不可访问非静态成员变量
a = 10;//可以访问
}
void Get()
{
//非静态成员函数可以访问静态成员变量
b = 10;
a = 10;
}
//不能用初始化列表初始化静态成员变量
//A(int b):a(10),b(20){}
//A(int b) : b(20) {}
A() { ++a; }
A(int b) { ++a; }
A(const A& aa) { ++a; }
~A() { --a; }
int GetACount() { return a; }
};
int A::a = 0;
int main()
{
A aa;
cout << aa.GetACount() << endl;
A aa1(10);
cout<<aa1.GetACount()<<endl;
A aa2(aa1);
cout<<aa2.GetACount() << endl;
}
//输出结果为
1
2
3
四.友元
友元提供了⼀种突破类访问限定符封装的⽅式,友元分为:友元函数和友元类,在函数声明或者类
声明的前⾯加friend,并且把友元声明放到⼀个类的⾥⾯。
友元类的关系是单向的,不具有交换性,⽐如A类是B类的友元,但是B类不是A类的友元。
友元类关系不能传递,如果A是B的友元, B是C的友元,但是A不是B的友元。
class A{};
class B
{
friend A;
};
五.内部类
如果⼀个类定义在另⼀个类的内部,这个内部类就叫做内部类。内部类是⼀个独⽴的类,跟定义在
全局相⽐,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
内部类默认是外部类的友元类。
从输出结果可以看出在计算类的大小时不会计算内部类。
六.匿名对象
只用类型(实参)定义出来的对象叫做匿名对象,相⽐之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象,匿名对象的生命周期很短,使用完就会被销毁。
文章就到这里了,如果你觉得写的还不错的话,请点赞收藏支持一下,如果有写错的地方,还请批评指正。