什么网站可以免费做宣传,深圳定制网站公司,发布软文广告,网站页面设计特点目录 1.再谈构造函数1.1构造函数赋值1.2初始化列表1.3explicit关键字 2.static成员2.1概念2.1静态成员变量2.2静态成员函数2.3特性 3.匿名对象4.友元函数4.1友元函数4.2友元类 5.内部类6.再次理解类和对象 1.再谈构造函数
首先我们先来回忆一下构造函数#xff1a; 构造函数是… 目录 1.再谈构造函数1.1构造函数赋值1.2初始化列表1.3explicit关键字 2.static成员2.1概念2.1静态成员变量2.2静态成员函数2.3特性 3.匿名对象4.友元函数4.1友元函数4.2友元类 5.内部类6.再次理解类和对象 1.再谈构造函数
首先我们先来回忆一下构造函数 构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用以保证每个数据成员都有 一个合适的初始值并且在对象整个生命周期内只调用一次。即构造函数其实就是帮我们对类的成员变量赋一个初值。 1.1构造函数赋值
下面我们来看个例子
class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
}; 虽然上述构造函数调用之后对象中已经有了一个初始值但是不能将其称为对对象中成员变量的初始化构造函数体中的语句只能将其称为赋初值而不能称作初始化。因为 初始化只能初始化一次而构造函数体内可以多次赋值。 下面我们再来看一个例子
class T
{
private:int _T1;int _T2;
}; 这里的int _T1; int _T2;是对成员变量_T1、 _T2的声明在这里只是声明类里这样两个成员变量。 那定义又是在哪里呢 这里是对对象整体的定义。 那么对象的每个成员变量又是什么时候定义的呢 我想这时老铁心里肯定这样想变量整体定义了它的成员不都也定义了吗成员不都是属于这个对象的吗 下面我们再看这个例子 在这里我们发现程序无法正常运行大家来想一下const修饰的变量有什么特点 是不是const修饰的变量必须在定义的时候初始化。 我想这个时候大家一定想到了 之前我们在讲解构造函数的时候说C11允许内置类型成员变量在类中声明的时候可以给缺省值。 这里程序能够运行了 但是这是C11才提出来的那C11之前呢如何解决这样的问题呢 所以我们必须要给成员变量也找一个定义的位置不然像const这样的成员变量不好处理。 那么成员变量定义到底在哪里呢
1.2初始化列表 面对上述问题我们的祖师爷还是把目标锁定在了构造函数。 在构造函数里面呢又搞了一个东西叫做——初始化列表。 初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。
举个例子
对于上面类中const int _a的初始化我们就可以放在初始化列表进行处理
class T
{public:T(int t1, int t2, int a): _T1(t1), _T2(t2), _a(a){}
private:int _T1;int _T2;const int _a 1;
};int main()
{T t(1,2,3);return 0;
}注意
每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次) 以下三种类成员变量必须放在初始化列表位置进行初始化
引用成员变量
const成员变量
没有默认构造函数的自定义类型成员 这里不难理解因为引用成员变量和const成员变量都必须在定义的时候初始化。 对于没有默认构造函数的自定义类型成员 因为默认生成的构造函数对内置类型不做处理对自定义类型会去调用它对应的默认构造函数不需要传参的构造函数都是默认构造函数所以如果自定义类型成员没有默认构造函数我们就需要自己去初始化它。 尽量使用初始化列表初始化因为不管你是否使用初始化列表成员变量都会在初始化列表定义。 成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关。
1.3explicit关键字
我们先举个例子
class T
{public:T(int t1): _T1(t1){}
private:int _T1;int _T2;};我们可以用这种方式去创建对象
int main()
{T t(1);return 0;
}除此之外还可以这样
int main()
{T t2 1;return 0;
}这个地方T t2 1;1是一个整型怎么可以直接去初始化一个类对象呢 其实这里是一个隐式类型转换。和内置类型之间的隐式类型转换转化是一样的会产生一个临时变量。 那这里T t2 1;是如何转换的呢 这里也会产生一个临时变量这个临时变量就是用1去构造出来的一个T类型的对象然后再用这个临时对象去拷贝构造我们的t2。 下面我们用一个小例子证明一下
class T
{public:T(int t1): _T1(t1){cout T(int t) endl;}T(const T t): _T1(t._T1){cout T(const T t) endl;}
private:int _T1;
};int main()
{T t2 1;return 0;
} 注意拷贝构造函数也是有初始化列表的因为拷贝构造函数是构造函数的一个重载形式。 那我们现在运行程序看T t2 1; 是不是先用1调构造函数创建一个临时变量然后再调拷贝构造构造t2。 这里确实调用了构造函数但是并没有调用拷贝构造函数。 那问题到底出在哪里了 其实C编译器针对自定义类型产生临时变量的情况会进行优化。编译器用1构造一个对象然后再去调拷贝构造效率受到影响所以优化成一步直接拿1去构造我们要创建的对象、 。 当然不一定所有的编译器都会优化但是一般比较新一点的编译器在这里都会优化。 在这里想告诉大家的是 构造函数不仅可以构造与初始化对象对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数还具有类型转换的作用。 那如果我们这里不想让它支持类型转换了有没有什么办法呢 这就要用到一个关键字——explicit 我们只需在对应得构造函数前面加上explicit关键字 对于单参数的构造函数是支持这种类型转换的那多参数的构造函数呢 这里C98是不支持多参数的构造函数进行隐式类型转换的。但是C11对这块进行了扩展使得多参数的构造函数也可以进行隐式类型转换 2.static成员
2.1概念 声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量;用 static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化 那么static成员有什么用呢 我们先来看一个小题目 实现一个类计算程序中创建出了多少个类对象。 老铁们思考一下可以怎么做 首先要创建一个类对象一定是通过构造函数或者拷贝构造创建的。那我们可以定义一个全局变量n初值为0。然后每次调用构造函数或者拷贝构造创建对象时就让n。 但是这种方法真的好吗 其实是不太好的首先这里我们定义一个全局变量首先它可能会发生命名冲突其次全局变量在哪都能访问C讲究封装。 我想这个时候老铁可能又想到一种方法 我们把统计个数的这个n变量放到类里面这样它就属于这个类域了然后如果不想让它在类外面被访问到我们可以把它修饰成私有的。 但是这种方法真的可行吗 如果直接放到类里面作为类的一个成员变量那它就属于对象但我们要统计程序中创建对象的个数这样我们每次创建一个对象n就会定义一次是不是不行啊不能让它属于每个对象应该让它属于整个类。 2.1静态成员变量
对于上述问题我们可以这样解决 在它前面加一个static修饰让它成为静态成员变量。那这样它就不再属于某个具体对象了而是存储在静态区为所有类对象所共享。规定静态成员变量的初始化定义的时候赋初值一定要在类外定义时不添加static关键字类中只是声明。 2.2静态成员函数 静态成员函数有一个特性静态成员函数没有隐藏的this指针不能访问任何非静态成员。 因为非静态成员是属于对象的都是通过this指针去访问的而静态成员函数是没有this指针的。 2.3特性 静态成员为所有类对象所共享不属于某个具体的对象存放在静态区静态成员变量必须在类外定义定义时不添加static关键字类中只是声明静态成员变量一定要在类外进行初始化类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问静态成员函数没有隐藏的this指针不能访问任何非静态成员静态成员也是类的成员受public、protected、private 访问限定符的限制 3.匿名对象
现在有这样一个类
class T
{public:T(int t1 0): _T1(t1){cout T(int t) endl;}~T(){cout ~T() endl;
private:int _T1;
}; 我们现在想要用这个类去创建对象除了我们之前学的方法之外其实我们还可以这样创建对象 这里我们用T这个类创建了一个匿名对象。 匿名对象的特点就是没有名字但是它的生命周期只在创建它的这一行。 但是要注意和临时变量一样如果我们用匿名对象去初始化一个引用的话它的生命周期就会被延长至该引用被销毁。并且这里肯定都要加const的因为临时变量和匿名对象都具有常性。 那匿名对象有什么用呢 现在有一个类Solution里面有一个非静态成员函数Sum_Solution我们知道想要调用类里面的非静态成员函数是需要通过对象去调用的: 那现在有了匿名对象我们就可以这样调用了 4.友元函数
友元提供了一种突破封装的方式有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。
友元分为:友元函数和友元类
4.1友元函数
在之前的类和对象的学习中我们讲过运算符重载 现在尝试去重载operator然后发现没办法将operator重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象才能正常使用。所以要将operator重载成全局函数。但又会导致类外没办法访问成员此时就需要友元来解决。operator同理。 友元函数可以直接访问类的私有成员它是定义在类外部的普通函数不属于任何类但需要在 类的内部声明声明时需要加friend关键字。 说明 友元函数可访问类的私有和保护成员但不是类的成员函数友元函数不能用const修饰友元函数可以在类里面的任何地方声明不受类访问限定符限制一个函数可以是多个类的友元函数友元函数的调用与普通函数的调用原理相同 4.2友元类
友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员。
友元关系是单向的不具有交换性。友元关系不能传递 如果C是B的友元 B是A的友元则不能说明C时A的友元。 友元关系不能继承在继承位置再给大家详细介绍。
5.内部类
概念:如果一个类定义在另一个类的内部这个内部类就叫做内部类。
比如这样
class A
{
private:int a;
public:class B {private:int b;};
};内部类并不属于外部类它和对应的外部类是相互独立的只是受外部类类域的限制。 对于上面那个类来说我们想拿A中的内部类B去创建对象这样是不行的 这样才行 内部类天生就是其对应的外部类的友元类。参见友元类的定义内部类可以通过外部类的对象参数来访 问外部类中的所有成员。但是外部类不是内部类的友元。 特性
内部类可以定义在外部类的public、protected、private都是可以的。注意内部类可以直接访问外部类中的static成员不需要外部类的对象/类名。sizeof(外部类)外部类和内部类没有任何关系。
6.再次理解类和对象
现实生活中的实体计算机并不认识计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体用户必须通过某种面向对象的语言对实体进行描述然后通过编写程序创建对象后计算机才可以认识。比如想要让计算机认识洗衣机就需要: 用户先要对现实中洗衣机实体进行抽象—即在人为思想层面对洗衣机进行认识洗衣机有什么属性有那些功能即对洗衣机进行抽象认知的一个过程经过1之后在人的头脑中已经对洗衣机有了一个清醒的认识只不过此时计算机还不清楚想要让计算机识别人想象中的洗衣机就需要人通过某种面相对象的语言(比如:C、Java、Python等)将洗衣机用类来进行描述并输入到计算机中经过2之后在计算机中就有了一个洗衣机类但是洗衣机类只是站在计算机的角度对洗衣 机对象进行描述的通过洗衣机类可以实例化出一个个具体的洗衣机对象此时计算机才能洗衣机是什么东西。用户就可以借助计算机中洗衣机对象来模拟现实中的洗衣机实体了。 在类和对象阶段大家一定要体会到类是对某一类实体(对象)来进行描述的描述该对象具有哪些属性哪些方法描述完成后就形成了一种新的自定义类型用然后用该自定义类型就可以实例化具体的对象。