当前位置: 首页 > news >正文

2.c++面向对象(五)

一.再谈构造函数

1.初始化列表

#include<iostream>
using namespace std;
#include<vector>
#include<list>typedef int DataType;
class Stack
{
public:Stack(size_t capacity = 4){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申请空间失败!!!");return;}_capacity = capacity;_size = 0;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}private:DataType* _array;int _capacity;int _size;
};class MyQueue
{
public:// Stack不具备默认构造。MyQueue也无法生成默认构造// 初始化列表// 初始化列表本质可以理解为每个对象中成员定义的地方// 所有的成员,你可以在初始化列表初始化,也可以在函数体内初始化// 1、引用 2、const 3、没有默认构造自定义类型成员(必须显示传参调构造)MyQueue(int n, int& rr):_pushst(n),_popst(n),_x(1),_ref(rr){_size = 0;//_x = 1;}private:// 声明Stack _pushst;Stack _popst;int _size;// 必须在定义时初始化const int _x;// 必须在定义时初始化int& _ref;
};int main()
{int xx = 0;MyQueue q1(10, xx);//MyQueue q2;const int y = 1;int& ry = xx;return 0;
}

能写初始化列表,就写初始化列表

const 变量必须在定义的时候进行初始化,初始化之后就不能改变了

引用 (&) 也是一样的,要在定义的时候进行初始化

1、引用     2、const      3、没有默认构造自定义类型成员(必须显示传参调构造)

2.尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量, 一定会先使用初始化列表初始化。

总结: 实践中,尽可能使用初始化列表进行初始化,不方便的再使用函数体进行初始化

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();return 0; // 通常建议添加main函数的返回值
}

可以看一下这个地方的答案:

选择D选项 (成员变量在类中进行声明的顺序是在类中进行定义的顺序)

二.隐式类型转换

1.隐式类型转换的使用

class A
{
public:// 单参数构造函数//explicit A(int a)A(int a):_a(a){cout << "A(int a)" << endl;}// 多参数构造函数A(int a1, int a2):_a(0),_a1(a1),_a2(a2){}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}private:int _a;int _a1;int _a2;
};int main()
{A aa1(1);// 拷贝构造A aa2 = aa1;// 隐式类型转换// 内置类型转换为自定义类型// 3构造一个A的临时对象,在用这个临时对象拷贝构造aa3// 编译器遇到连续构造+拷贝构造->优化为直接构造A aa3 = 3;// raa 引用的是类型转换中用3构造的临时对象 const A& raa = 3;A aaa1(1, 2);A aaa2 = { 1, 2 };const A& aaa3 = { 1, 2 };return 0;
}

这里编译器做了优化将连续的构造和拷贝构造,转化成为直接构造

raa 引用的是类型转换中用3构造的临时对象

上述隐式类型感觉没啥用处,下面我们来举一个列子,来进行观测一下

class A
{
public:// 单参数构造函数//explicit A(int a)A(int a):_a(a){cout << "A(int a)" << endl;}// 多参数构造函数A(int a1, int a2):_a(0),_a1(a1),_a2(a2){}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}private:int _a;int _a1;int _a2;
};class Stack
{
public:void Push(const A& aa){//...}//...
};int main()
{Stack st;A a1(1);st.Push(a1);A a2(2);st.Push(a2);st.Push(2);st.Push(4);A aa1(1, 2);st.Push(aa1);st.Push({ 1,2 });
}

1.单参数的构造函数

2.多参数的隐式类型转换

C++11 中,我们一般使用的是{}进行初始化

我们的缺省值,除了可以上面那样给,还可以向下面这样给:

class A
{
public:// 单参数构造函数//explicit A(int a)A(int a):_a(a){cout << "A(int a)" << endl;}// 多参数构造函数A(int a1, int a2):_a(0),_a1(a1),_a2(a2){}A(const A& aa):_a(aa._a){cout << "A(const A& aa)" << endl;}private:int _a;int _a1;int _a2;
};class BB
{
public:BB(){}private:// 声明给缺省值int _b1 = 1;int* _ptr = (int*)malloc(40);Stack _pushst = 10;A _a1 = 1;A _a2 = { 1,2 };A _a3 = _a2;
};int main()
{BB bb;return 0;
}

2.explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值 的构造函数,还具有类型转换的作用。

class Date
{
public:// 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译explicit Date(int year): _year(year){}/*// 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递,没有使用explicit修饰,具有类型转换作用// explicit修饰构造函数,禁止类型转换explicit Date(int year, int month = 1, int day = 1): _year(year), _month(month), _day(day){}*/Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day;
};void Test()
{Date d1(2022);// 用一个整形变量给日期类型对象赋值// 实际编译器背后会用2023构造一个无名对象,最后用无名对象给d1对象进行赋值d1 = 2023;// 将1屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数,禁止了单参构造函数类型转换的作用
}

三.static成员

1.静态成员变量

static变量是存储在静态区中的,static 对象是不能给缺省值(缺省值 是给初始化列表的,初始化列表是给对象成员的)

只有声明没有定义是会报错的

我们可以统计有多少个对象还在使用:

class A
{
public:A() { ++_scount;}A(const A & t) { //GetCount();++_scount;}~A(){ //--_scount;}// 没有this指针,只能访问静态成员static int GetCount(){//_a1 = 1;return _scount;}
private:// 声明int _a1 = 1;int _a2 = 1;
// public:// 声明// 静态区,不存在对象中// 不能给缺省值,因为缺省值是给初始化列表// 他在静态区不在对象中,不走初始化列表// 属于所有整个类,属于所有对象static int _scount;
};// 定义
int A::_scount = 0;A func()
{A aa4;return aa4;
}int main()
{A aa1;cout << sizeof(aa1) << endl;A aa2;A aa3(aa1);func();//aa1._scount++;//cout << A::_scount << endl;cout << A::GetCount() << endl;return 0;
}

2.静态成员函数

用static修饰成员函数的时候,没有this指针,我们没有创建对象就能直接使用

我们可以通过公有的成员函数进行调用

3.特性

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区

2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明

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

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

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

4.题目

1.求 1+2+3+...+n

https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

#include <regex>
class sum
{
public:sum(){_sum += _i;++_i;}static int get_ret(){return _sum;}
private:static int _i;static int _sum;
};int sum::_sum = 0;
int sum::_i = 1;class Solution {
public:int Sum_Solution(int n) {sum arr[n];return sum::get_ret();}
};

四.友元

友元提供了一种突破封装的方式,有时提供了便利。

但是友元会增加耦合度,破坏了封装,所以 友元不宜多用。 友元分为:友元函数和友元类

1.友元函数

#include <iostream>
using namespace std;class Date
{// 友元声明,允许外部函数访问私有成员friend ostream& operator<<(ostream& _cout, const Date& d);friend istream& operator>>(istream& _cin, Date& d);public:// 带默认参数的构造函数Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}private:int _year;   // 年int _month;  // 月int _day;    // 日
};// 重载输出运算符,用于打印Date对象
ostream& operator<<(ostream& _cout, const Date& d)
{_cout << d._year << "-" << d._month << "-" << d._day;return _cout;
}// 重载输入运算符,用于输入Date对象
istream& operator>>(istream& _cin, Date& d)
{_cin >> d._year;_cin >> d._month;_cin >> d._day;return _cin;
}int main()
{Date d;         // 使用默认构造函数创建对象cin >> d;       // 输入日期cout << d << endl;  // 输出日期return 0;
}

说明:

友元函数可访问类的私有和保护成员,但不是类的成员函数

友元函数不能用const修饰

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

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

友元函数的调用与普通函数的调用原理相同

2.友元类

class Time
{// 声明 Date是Time的友元// Date中可以访问Time的私有// 但是Time中不能访问Date的私有friend class Date;
public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){_t._hour++;}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

五.内部类

class A
{
private:static int k;int h;
public:void func(){}
private:// 内部类// 独立的类,放到A里面// 仅仅受到类域限制class B // B天生就是A的友元{public:void foo(const A& a){cout << k << endl;//OKcout << a.h << endl;//OK}private:int _b;};
};int main()
{cout << sizeof(A) << endl;A a1;//A::B b1;return 0;
}

a对象里面不存在一个b对象

所以a的大小只和自己有关

我们可以在a类里面定义b这个类(B天生是A的友元),所以我们可以将B理解成为A的专属

C++不太喜欢内部类

还是这个题目,我们可以尝试使用内部类来进行解决:

https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId=13&tqId=11200&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

class Solution {class sum{public:sum(){_ret += _i;++_i;}};static int _i;static int _ret;
public:int Sum_Solution(int n) {sum arr[n];return _ret;}
};int Solution::_i = 1;
int Solution::_ret = 0;

http://www.dtcms.com/a/473909.html

相关文章:

  • python中的一些运算符
  • 【嵌入式面试题】boss收集的11道,持续更新中
  • 保证样式稿高度还原
  • 网站建设 源码怎么注册公司名
  • [xboard] 34 buildroot 的overlay机制
  • 某公司站点的挖掘实战分享
  • 第三方和审核场景回调还是主动查询
  • Git基本命令的使用(超详细)
  • NC40 链表相加(二)
  • 网安面试题收集(3)
  • JetLinks设备接入的认识与理解
  • 从HashMap到ConcurrentHashMap深入剖析Java并发容器的演进与实战
  • 做一组静态页面网站多少钱网站源码上传到哪个文件夹
  • 威海市城乡建设局网站网络整合营销服务商
  • 从报头到路由器——【网络编程】详解 IP 协议:报头字段、路由器功能、网段划分和分片传输
  • 网站验证北京建网站开发
  • 设计模式篇之 装饰器模式 Decorator
  • 虚幻引擎虚拟制片入门教程 之 创建项目及启用插件
  • 淳安县建设网站王璞网站开发实战答案
  • Linux禁用自带键盘和触摸板(无需每次开机重置)
  • 149、【OS】【Nuttx】【周边】效果呈现方案解析:VSCode 打开外部链接(二)
  • Apache Commons IO:文件流处理利器,让Java IO操作更简单
  • 哪个网站做简历免费自己做免费网站
  • 医院预约挂号|基于Java+vue的医院预约挂号系统小程序的设计与实现(源码+数据库+文档)
  • 翻转二叉树---超全详细解
  • AI智能体全球应用调查报告:从“对话”到“做事”的变革
  • Linux网络之----网络编程
  • [Power BI] CALCULATETABLE函数
  • 3494. 酿造药水需要的最少总时间
  • 沐风老师3DMAX科研绘图插件DNA生成器使用方法详解