- 1. 再谈构造函数
- 2. Static成员
- 3. 友元
- 4. 内部类
- 5. 再次理解封装
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;
};
#pragma once
#include<bits/stdc++.h>
using namespace std;
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _size;
int _capacity;
};
class MyQueue
{
private:
Stack _pushst;
Stack _popst;
int _size;
};
int main()
{
MyQueue q;
}
上面这个代码,MyQueue的_size就去掉系统的默认构造,_pushst;和_popst;就去掉Stack的默认构造,假如这里把构造改了
#pragma once
#include<bits/stdc++.h>
using namespace std;
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _size;
int _capacity;
};
class MyQueue
{
private:
Stack _pushst;
Stack _popst;
int _size;
};
这里把缺省值去了这里的就没有了默认构造
那么这个时候就会报错,假如我Stack 的构造函数不能改了这里应该怎么做呢?
1.2 初始化列表
class MyQueue
{
public:
MyQueue(int n=10)
:_pushst(n)
,_popst(n)
,_size(0)
{}
private:
Stack _pushst;
Stack _popst;
int _size;
};
用初始化列表就可以解决这个问题
const修饰的成员变量需要在初始化列表中初始化不能在构造函数里面初始化
const修饰的成员必须在定义的时候初始化
因为const修饰的变量只有一次初始化的机会,初始化以后不能更改
如上图_x成员无法在构造函数里面初始化
必须在初始化列表里面初始化
引用也是一样的道理,他和const修饰的变量一样
总结:
所有的成员都可以在初始化列表里面初始化,也可以在函数体内初始化,但有三类必须在初始化列表里面初始化
1.引用
2.const修饰的成员
3.没有默认构造的自定义成员
假如把代码改成这样
#pragma once
#include<bits/stdc++.h>
using namespace std;
typedef int DataType;
class Stack
{
public:
Stack(int capacity=4)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(DataType data)
{
_array[_size] = data;
_size++;
}
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
DataType* _array;
int _size;
int _capacity;
};
class MyQueue
{
public:
MyQueue()
{
_size = 0;
}
private:
Stack _pushst;
Stack _popst;
int _size;
};
那么上面三个都会初始化吗?
上面MyQueue构造函数不是只对_size做了初始化吗为什么其他俩个成员也被初始化了?
初始化列表不管写不写,会按每个成员的顺序都去走一遍初始化列表
练习
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();
}
A. 输出1 1
B.程序崩溃
C.编译不通过
D.输出1 随机值
这里的答案是D
初始化列表的顺序是按照声明的顺序
1.3 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修饰构造函数,禁止了单参构造函数类型转换的作
用
}
上述代码可读性不是很好,用explicit修饰构造函数,将会禁止构造函数的隐式转换。
2. static成员
static修饰过的成员无法给缺省值因为缺省值是给初始化列表的static修饰了以后不在栈上所有这里会报错
注意这里的int很容易忘记
下面输出是多少?
答案是8
被static修饰的成员不是存放在类里面他在静态区
被static修饰的函数没有this指针无法访问成员
可以用::来调用
面试题:
class A
{
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{
return _scount;
}
private:
static int _scount;
};
int A::_scount = 0;
void TestA()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
}
3. 友元
3.1友元函数
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
ostream& operator<<(ostream& _cout)
{
_ cout << _year << "-" << _month << "-" << _day << endl;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
这里就会报错因为成员是私有类型无法访问
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
//class A
//{
//public:
//
// A()
// {
// _scount++;
// }
// A(const A& t)
// {
// _scount++;
// }
// static int Getcount()
// {
//
// return _scount;
// }
//private:
// int _a1;
// int _a2;
// static int _scount;
//};
//int A::_scount = 0;
//A Test()
//{
// A a3;
// return a3;
//}
//
//int main()
//{
// A a1;
// A a2 = a1;
// Test();
// cout << A::Getcount();
//}
class Date
{
friend ostream& operator<<(ostream& _cout,Date d);
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout,Date d)
{
_cout <<d. _year << "-" << d._month << "-" << d._day << endl;
return _cout;
}
int main()
{
Date d1(1, 2, 3);
cout << d1;
}
这样才是对的
可以理解成变成了全局,这里的参数必须加
因为不加编译器分不清是哪一个类
成功运行
3.2友元类
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
员变量
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)
{}
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;
};
4. 内部类
class A
{
private:
static int k;
int h;
public:
class B
{
private:
int _a;
};
};
int main()
{
cout << sizeof(A);
return 0;
}
输出上面?
A类和B类他们俩个是平行的关系且都是独立的存在
仅仅受到类域的限制
他必须要这样定义
如果B类在A类的私有里面就无法创建
class A{
private:
static int k;
int h;
public:
class B // B天生就是A的友元
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
内部类可以访问外部类,但外部类无法访问内部类,可以理解成内部类又封装了一下安全性就更好了
5.匿名对象
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)" << endl;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a;
};
int main()
{
A a1;
return 0;
}
这个就叫有名对象
int main()
{
A(1);
A;
return 0;
}
这种就叫匿名对象
有名和匿名的区别就是:
有名的生命周期是整个函数体
匿名的生命周期是只有这一行
class Solution
{
public:
int Sum_Solution(int n)
{
return n;
}
};
int main()
{
Solution().Sum_Solution(10);
return 0;
}
这种可以这样调用