商丘河南网站建设,织梦后台发布了网站没显示,网站做联盟收入,上海网站开发与设类与对象#xff08;下#xff09; 目录类与对象#xff08;下#xff09;一、再谈构造函数1.构造函数体赋值2.初始化列表定义#xff1a;注意点#xff1a;总结#xff1a;3.explicit关键字引入#xff1a;explicit#xff1a;二、 static成员回顾#xff1a;static…类与对象下 目录类与对象下一、再谈构造函数1.构造函数体赋值2.初始化列表定义注意点总结3.explicit关键字引入explicit二、 static成员回顾static作用引入面试题特征三、友元四、内部类(了解)五、匿名对象引入作用六、拷贝对象时的一些编译器优化再次理解面向对象一、再谈构造函数
1.构造函数体赋值 在“类与对象中”我们了解到在创建对象时编译器通过调用构造函数给对象中各个成员变量一个合适的初始值如下 class Date
{
public:Date(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;int _day;
};虽然上述构造函数调用之后对象中已经有了一个初始值但是不能将其称为对对象中成员变量的初始化 构造函数体中的语句只能将其称为赋初值而不能称作初始化因为初始化只能初始化一次而构造函数体内可以多次赋值
因此在次我们引入了初始化列表
2.初始化列表
定义
初始化列表以一个冒号开始接着是一个以逗号分隔的数据成员列表每个**“成员变量”**后面跟 一个放在括号中的初始值或表达式。 特点 ①每个成员变量都要走初始化列表就算不显示写初始化列表也会走初始化列表 ②如果不显示写初始化列表声明处给了缺省值就会使用缺省值初始化如果显示写了初始化列表即使给了缺省值也不会使用 步骤如下 1.对于内置类型优先使用初始化列表没显示写初始化列表有缺省值用缺省值没有就用随机值 2.自定义类型调用它的默认构造函数如果没有默认构造就报错。 那么对于我们熟知的Date类其初始化列表如下
class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}
private:int _year;int _month;int _day;
};注意点
1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2.类中包含一下成员必须放在初始化列表位置初始化 引用成员变量const成员变量自定义类型成员(且该类没有默认构造函数时) 其实对于const和引用只有一次初始化的机会就只能在定义的时候进行初始化
例如
class A
{
public:A(int a):_a(a){}
private:int _a;
};class B
{
public:B(int a, int ref):_aobj(a), _ref(ref), _n(10){}
private:A _aobj; // 没有默认构造函数int _ref; // 引用const int _n; // const
};3.尽量使用初始化列表因为不管你是否使用初始化列表对于自定义类型成员变量一定会先使用初始化列表初始化
4.成员变量在类中声明次序就是其在初始化列表中的初始化顺序与其在初始化列表中的先后次序无关
例如分析下面这段代码的输出结果
class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout_a1 _a2endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}分析过程如下 总结 1、尽量使用初始化列表初始化 2、一个类尽量提供默认构造推荐提供全缺省 3.explicit关键字
引入
构造函数不仅可以构造与初始化对象 对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数还具有类型转换的作用
// 1. 单参构造函数没有使用explicit修饰具有类型转换作用Date(int year):_year(year){}// 2. 虽然有多个参数但是创建对象时后两个参数可以不传递没有使用explicit修饰具有类型转换作用Date(int year, int month 1, int day 1): _year(year), _month(month), _day(day){}void Test()
{//隐式类型的转换int i0;double di;const double rdi;//单参数构造函数Date d1(2022);Date d22022;const Date d52022;//拷贝构造Date d3(d1);Date d4d1;
} 这里发生了隐式类型的转换从int类转换为了Date类会涉及到中间开辟一个临时对象/变量再会用这个临时对象来拷贝构造它通常编译器优化后即为直接构造了 若不想发生类型转换即可使用explicit关键字
explicit
用explicit修饰构造函数将会禁止构造函数的隐式转换 若是多参数构造C11支持即
Date d1{2023,3,1};
//等价于
Date d2(2023,3,1);注意这里的等价于只是说明它们的功能结果相同而实际意义却不同第一种方式即为多参隐式构造而第二种方式是直接构造 二、 static成员
回顾static作用 1、在函数体内一个被声明为静态的变量在这一函数被调用的过程中维持其值不变维持着上次函数调用后的值而不是每次函数调用后值都重新进行声明 2、在模块内(但在函数体外)一个被声明为静态的变量可以被模块内所有函数访问但不能被模块外其他函数访问。它是一个本地的全局变量 3、在模块内一个被声明为静态的函数只可被这一模块内的其他函数调用。也就是这个函数被限制在声明它的模块的本地范围内使用 声明为static的类成员称为类的静态成员用static修饰的成员变量称之为静态成员变量用static修饰的成员函数称之为静态成员函数。静态成员变量一定要在类外进行初始化
引入面试题 【面试题】实现一个类计算程序中创建出了多少个类对象 class A
{
public://构造函数A(){_count;}//拷贝构造A(A _a){_count;}//析构函数~A(){_count;}//定义静态变量_countstatic int GetCount(){return _count;}
private:static int _count;
};
//在类外声明静态成员变量并给予初始化
int A::_count 0; //静态成员变量定义初始化void test()
{cout A::GetCount() endl;A a1;A a2;A a3;cout A::GetCount() endl;
}首先这里可以用全局变量来实现我们的目标要求 那为何我们要把它设为static呢原因是全局变量容易被更改不安全
特征 1.静态成员为所有类对象所共享不属于某个具体的对象存放在静态区 2.静态成员变量必须在类外定义定义时不添加static关键字类中只是声明 3.类静态成员即可用 类名**::静态成员 或者 对象.**静态成员来访问 4.静态成员函数没有隐藏的this指针不能访问任何非静态成员 5.静态成员也是类的成员受public、 protected、 private 访问限定符的限制 因此由于static具有如上特性它的定义方法如下 可以注意到首先在类外定义不加static关键字在类中声明变量由于它由private限定因此我们再提供它的Get方法而在使用时 我们用get方法得到它的值再用类名::静态成员的方式对它进行访问用于输出它的值 【问题一】静态成员函数可以调用非静态成员函数吗 静态成员函数没有隐含的this指针不能访问任何非静态成员并且静态成员函数属于类而不是某个对象。 【问题二】非静态成员函数可以调用类的静态成员函数吗 非静态成员函数可以调用类的静态成员函数。 三、友元
友元提供了一种突破封装的方式有时提供了便利但是友元会增加耦合度破坏了封装所以友元不宜多用
友元分为 友元函数和友元类
例如在“面向对象中”我们写到的流插入和流提取就用到了友元函数
//友元声明类中的任意位置friend inline ostream operator(ostream out, const Date d);friend inline istream operator(istream in, Date d);// 流插入的重载
inline ostream operator(ostream out, const Date d)
{out d._year 年 d._month 月 d._day 日 endl;return out;
}// 流提取的重载
inline istream operator(istream in, Date d)
{in d._year d._month d._day;return in;
}说明 友元函数 友元函数可访问类的私有和保护成员但不是类的成员函数 友元函数不能用const修饰 友元函数可以在类定义的任何地方声明 不受类访问限定符限制 一个函数可以是多个类的友元函数 友元函数的调用与普通函数的调用原理相同 友元类 友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的非公有成员 友元关系是单向的不具有交换性比如上述Time类和Date类在Time类中声明Date类为其友元类那么可以在Date类中直接 访问Time类的私有成员变量但想在Time类中访问Date类中私有的成员变量则不行。 友元关系不能传递如果C是B的友元 B是A的友元则不能说明C时A的友元 友元关系不能继承在继承位置再给大家详细介绍 四、内部类(了解)
所谓内部类即是类中嵌套了类
概念如果一个类定义在另一个类的内部这个内部类就叫做内部类。内部类是一个独立的类它不属于外部类更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。 注意内部类就是外部类的友元类参见友元类的定义内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元
例如
class A
{
private:static int k;int h;
public:class B // B天生就是A的友元{public:void func(const A a){cout k endl; //OKcout a.h endl; //OK}};
};
int A::k 1; //B天生是A的友元体现
int main()
{cout sizeof(A) endl;A aa;A::B bb; //指定域才能访问
}五、匿名对象
引入 我们构造一个类的对象通常有多种方式 对于以下类
class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}~A(){cout ~A() endl;}
private:int _a;
};
class Solution {
public:int Sum_Solution(int n) {//...return n;}
};定义对象有以下方式
int main()
{A aa1; //有名对象A(); //匿名对象A aa2(2); //有名对象A aa3 2; //有名对象return 0;
}注意不能这么定义对象因为编译器无法识别下面是一个函数声明还是对象定义
A aa1();但是我们可以这么定义匿名对象匿名对象的特点不用取名字但是他的生命周期只有这一行我们可以看到下一行他就会自动调用析构函数 作用
那么匿名对象有什么作用呢
// 匿名对象在这样场景下就很好用当然还有一些其他使用场景
Solution().Sum_Solution(10);正常应该是
Solution so;
so.Sum_Solution(10);这里我们采用创建匿名对象调用函数
六、拷贝对象时的一些编译器优化
在传值和传返回值的过程中一般C编译器会做一些优化减少对象的拷贝这个在一些场景下是非常有用的
class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}A(const A aa):_a(aa._a){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this ! aa){_a aa._a;}return *this;}~A(){cout ~A() endl;}
private:int _a;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}
int main()
{// 传值传参A aa1;f1(aa1);cout endl;// 传值返回f2();cout endl;// 隐式类型连续构造拷贝构造-优化为直接构造f1(1);// 一个表达式中连续构造拷贝构造-优化为一个构造f1(A(2));cout endl;// 一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造A aa2 f2();cout endl;// 一个表达式中连续拷贝构造赋值重载-无法优化aa1 f2();cout endl;return 0;
}【场景一】
一个表达式中连续构造拷贝构造编译器会优化为一个构造
【场景二】 一个表达式中连续拷贝构造拷贝构造-优化一个拷贝构造
【场景三】 一个表达式中连续拷贝构造赋值重载-无法优化。
再次理解面向对象
对现实世界的映射——描述世界