類與對象(3)
再學構造函數
構造函數體賦值
先構造一個帶有全缺省的日期類
class Date
{
public:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
上述構造函數調用之後,對象中已經有了一個初始值,但是不能將其稱作爲類對象成員的初始化,構造函數體中的語句只能將其稱作爲賦初值,而不能稱作初始化。因爲初始化只能初始化一次,而構造函數體內可以多次賦值。
初始化列表
class Date
{
public:
Date(int year,int month,int day)
:_year(year)
,_month(month)
,_day(day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
初始化列表以:
開始,接着用一個,
分隔的數據成員列表,每個“成員變量”後面跟一個放在括號內的初始值或者表達式
注意事項:
1、每個成員變量在初始化列表中只出現一次(初始化只能初始化一次)
2、類中的成員:“引用成員變量”,“const成員變量”,“類類型成員(該類沒有默認構造函數)”
class skr
{
public:
skr(int a)
:_a(a)
{
}
private:
int _a;
};
class cxk
{
public:
cxk(int a,int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{
}
private:
skr _aobj;
int& _ref;
const int _n;
}
3、儘可能使用初始化列表初始化,因爲不管你是否使用初始化列表,對於自定義類型成員變量,一定會先使用初始化列表初始化。
class Time
{
public:
Time(int hour = 0)
:_hour(222)
{
cout << "Time()" << endl;
}
private:
int _hour;
};
class Date
{
public:
Date(int day)
{}
private:
int _day;
Time _t;
};
int main()
{
Date d(1);
}
打印結果如圖
4、成員變量初始化列表的順序按照在類中聲明成員變量的順序,與初始化列表的順序無關
class Array
{
public:
Array(int size)
:_size(10)
, _array((int*)malloc(sizeof(int)*_size))
{}
private:
int* _array;
int _size;
};
explicit關鍵字
用explicit修飾構造函數,將會禁止單個參數的構造函數的隱式轉換
static成員
聲明爲static的類成員稱爲類的靜態成員,用static修飾的成員變量,稱之爲靜態成員變量;
用static修飾的成員函數,稱之爲靜態成員函數。靜態的成員變量一定要在類外進行初始化
#include <iostream>
using namespace std;
class A
{
public:
A() {++_scount;}
A(const A& t) {++_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;
}
int main()
{
TestA();
return 0;
}
注意!!
注意!!
注意!!
靜態的成員變量一定要在類外進行初始化
static特性
1、靜態成員爲所有類對象所共享,不屬於某個具體的實例
2、靜態成員變量必須在類外定義,定義時不添加static關鍵字
3、類靜態成員即可用類名::靜態成員
或者對象.靜態成員
來訪問
4、靜態成員函數沒有隱藏的this指針,不能訪問任何非靜態成員
5、靜態成員和類的普通成員一樣,也有public、protected、private,3種訪問級別,也可以具有返回值,const修飾符等參數。
C++11的成員初始化
C++11支持非靜態成員變量在聲明時,直接初始化
class A
{
public:
void Print()
{
cout << a << endl;
cout << b._b<< endl;
cout << p << endl;
}
private:
// 非靜態成員變量,可以在成員聲明時,直接初始化。
int a = 10;
B b = 20;
int* p = (int*)malloc(4);
static int n;
};
友元
友元函數
在重載運算符中,我們沒辦法將operator<<重載成員成員函數。因爲cout的輸出流對象和隱含的this指針在搶佔第一個參數位置。this指針默認是第一個參數也就是左操作數了。但是實際中使用cout需要是第一個形參對象,才能正常使用。所以我們要將operator<<重載成全局函數。但是這樣的話,又會導致類外沒有辦法訪問成員,那麼這裏就需要友元來解決了。operator同理。
友元函數可以直接訪問類的私有成員,它是定義在類外部的普通函數,不屬於任何類,但需要在類的內部聲明,聲明時需要加friend關鍵字。
#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, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout,const Date& d)
{
_cout<<d._year<<"-"<<d._month<<"-"<<d._day;
return _cout;
}
//這裏不能加const,如果加了const就不能對對象d的成員變量進行修改了
istream& operator>>(istream& _cin,Date& d)
{
_cin>>d._year;
_cin>>d._month;
_cin>>d._day;
return _cin;
}
int main()
{
Date d(2017,12,24);
cin>>d;
cout<<d<<endl;
return 0;
}
友元函數說明:
1、友元函數可訪問類的私有成員,但不是類的成員函數
2、友元函數不能用const修飾
3、友元函數可以在類定義的任何地方聲明,不受類訪問限定符限制
4、一個函數可以是多個類的友元函數
5、友元函數的調用與普通函數的調用和原理相同
友元類
友元類的所有成員函數都可以是另一個類的友元函數,都可以訪問另一個類中的非公有成員。
-
友元關係是單向的,不具有交換性
有A類和B類,在A中聲明B類爲其友元類,那麼可以在B類中直接訪問A類的私有成員變量,但想在A類中訪問B類中私有的成員變量則不行
-
友元關係不能傳遞
如果B是A的友元,C是B的友元,則不能說明C是A的友元
內部類
如果一個類定義在另一個類的內部,這個內部類就叫做內部類。注意此時這個內部類是一個獨立的類,它不屬於外部類,更不能通過外部類的對象去調用內部類。外部類對內部類沒有任何優越的訪問權限。
內部類是外部類的友元類。
內部類特性
1、內部類可以定義在外部類的public、protected、private都是可以的。
2、注意內部類可以直接訪問外部類中的static、枚舉成員,不需要外部類的對象/類名。
3、sizeof(外部類)=外部類,和內部類沒有任何關係。
牛客的一道小練習
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。
https://www.nowcoder.com/practice/7a0da8fc483247ff8800059e12d7caf1?tpId
基本上我們能想到的方法都被限制了,那麼這時候就可以考慮構造函數每次調用時都會+1
class Solution {
public:
class Sum
{
public :Sum()
{
_sum += _i;
_i++;
}
};
int Sum_Solution(int n) {
_i = 1;
_sum = 0;
Sum array[n];
return _sum;
}
static size_t _sum;
static size_t _i;
};
size_t Solution::_sum = 0;
size_t Solution::_i = 0;