五.类的静态成员
1.与属于对象的非静态成员不同,类的静态成员是属于类的,而不属于对象。
1)静态成员变量的定义和初始化,只能在类的外部进行,而不能够在构造函数(创造对象)中进行。所有在class里面的都只是声明。
2)静态成员变量被该类的多个对象实例所共享。
2.静态成员函数只能访问静态成员,不能访问非静态成员。而非静态成员函数既可以访问非静态成员,也可以访问静态成员。
4)访问静态成员既可以通过类也可以通过对象,但是最好用类。
5)静态成员和非静态成员一样也受类的作用域和访问控制的限制。
///////////////////////////////////
#include<iostream>
using namespace std;
class Account{
public:
Account(const string& name,double balance=0):m_name(name),m_balance(balance){}
void print(void) const{
cout<<m_name<<", "<<m_balance<<endl;
}
void save(double money){
m_balance+=money;
}
bool withdray(double money){
if(money>m_balance)
return false;
m_balance-=money;
return true;
}
void settle(void){
m_balance_=(1+m_rate);
}
//静态成员函数
static void adjust(double rate){
m_rate=rate;//只能访问静态成员
}
private:
string m_name;
double m_balance;
//静态成员变量
static double m_rate;
};
//在类的外部定义并初始化,不能在声明的时候初始化,访问控制不限制定义只限制赋值,运算等。
double Account::m_rate=0.01;
int main(void){
Account acc("张飞");
acc.print();
acc.save(10000);
if(!acc.withdray(5000))
cout<<"取款失败!"<<endl;
else
acc.print();
//静态成员函数调用
Account::m_rate=0.02;
acc.m_rate=0.02;
acc.adjust(0.02);
return 0;
}
//////////////////////////////////
2.单例模式
希望一个类只能创建出一个对象来,创建不出第二个实例来。
1.通过构造函数私有化,禁止在类外部自由创建对象。
2. 通过静态成员控制对象的创建。
3.通过引用计数控制对象的销毁。
#include<iostream>
using namespace std;
class Singleton{
//静态成员函数用来调用构造函数
static Singleton* Create(){
if(!m_inst)
m_inst=new Singleton;
return m_inst;
}
void Destroy(void){
delete this;
}
//构造函数私有化
private:
Singleton(void){
}
//拷贝构造也私有化
Singleton(const Singleton& that){}
static Singleton* m_inst;
~Singleton(void){
m_inst=NULL;
}
};
singleton* Singleton::m_inst=NULL;
int main(){
Singleton * ps1=Singleton::Create();
cout<<(void*)ps1<<endl;
Singleton * ps2=Singleton::Create();
cout<<(void*)ps2<<endl;
}
六。类的成员指针
Student student();
string *pstr=&student.m_name;
pstr不是成员指针。
1.指向成员变量的指针
定义:成员变量的类型 类名::*指针变量名。
String Student::*pstr;
赋值:指针变量名=&类名::成员变量名。
pstr=&Student::m_name;
解引用:对象.*指针变量名,对象指针->*指针变量名。
cout<<student.*pstr;
cout<<ps->*pstr;
实际上,成员变量指针是类中成员变量在类中的偏移量。所以不同的对象调用成员指针可以显示不同对象中的内容。
2.指向成员函数的指针
定义:成员函数返回类型(类名::*指针变量名)(形参表);
void (Student::*pfunc)(void);
赋值:指针变量名=&类名::成员函数名。
pfunc=&Student::who;
解引用:(对象.*指针变量名)(实参表).(对象指针->*指针变量名)(实参表)
(s1.*pfunc)();
(ps->pfunc)();
3.指向静态成员的指针与普通指针没有区别,不需要特殊的语法。
////////////////////////////////
#include<iostream>
using namespace std;
class Student{
public:
Student(const string& name=" ",int age=0):m_name(name),m_age(age){}
void who(){
}
string m_name;
int m_age;
};
int main(void){
string Student::*pstr=&Student::m_name;
int Student::*pn=&Student::m_age;
Student s1("张飞",28);
cout<<s1.*pstr<<','<<s1.*pn<<endl;
Student* ps=new Student("赵云",25);
cout<<ps->*pstr<<','<<ps->*pn<<endl;
delete ps;
//成员函数指针
void (Student::*pfunc)(void) const=&Student::who;
(s1.*pfunc)();
}
第四课 操作符重载
一. 操作符与操作符函数
对于表达式L#R,如果L和R中至少有一个是类类型的对象,那么编译器就会将以上表达式处理为如下函数调用:
L.operator#(R); //成员函数
operator#(L,R); //全局函数
而该函数的返回值就是表达式的值。
二.双目操作符重载
复数:(3+2i)+(4+5i)=7+7i
用复数类表达复数,可以通过重载运算符的方法,实现复数相加的规则。
////////////////////////////////
#include<iostream>
using namespace std;
class Complex{
public:
Complex(int r=0,int i=0):m_r(r),m_i(i){}
void print() const{
cout<<'('<<m_r<<'+'<<m_i<<"i)"<<endl;
}
///重载运算符函数
//使用引用避免拷贝构造函数
//一个非常量引用不能引用常量对旬
//后面的const的目的是兼顾调用这个函数的是一个常量对象c1,即左操作数,形参表里面的const是兼顾c2即右操作数是一个常量的情况。最前面那个const是为了防止将返回值作为左值,一个常量对象是不能被赋值的。
const Complex operator+ ( const Complex& c) const{
complex sum;
sum.m_r=m_r+c.m_r;
sum.m_i=m_i+c.m_i;
return sum;
//return Complex(m_r+c.m_r,m_i+c.m_i);
}
private:
int m_r;//实部
int m_i;//虚部
};
int main(){
//如果c1是常量
Complex c1(1,2);
c1.print();
///c2如果是常量
Complex c2(3,4);
//重载加法运算符
Complex c3=c1+c2; //c3=c1.operator+(c2)
///////////////
Complex ca(10,10),cb(20,20),cc(30,30);
(ca+cb)=cc;//编译不会出错,但是不正确。让其编译出错。让其返回类型变成const
return 0;
}
返回类型为const:防止将函数的返回值用作左值
形参类型为const:接受常量型的右操作数
函数类型为const:处理常量型的左操作数
Complex& operator+=(const Complex& c){
m_r+=c.m_r;
m_i+=c.m_i;
return *this; //返回逢引用的目的是为了让它可以做左值被赋值。
}
c5+=c2;
+=运算是可以做左值的。
Complex ca(10,10),cb(20,20),cc(30,30);
(ca+=cb)=cc;
三.输入输出操作符重载
Complex a();
cout<<a; //cout.operator<<(a);
//operator<<(cout,a);
cin>>a;
///////////////////////////////////
把下面这个函数声明为复数类的友元,那么这个函数便可以访问复数类的私有成员。
class Complex{
public:
complex(int r,int i):m_r(r),m_i(i){}
private:
int m_r;
int m_i;
//以下函数是Complex类的友元
friend ostream& operator<< (ostream&,const Complex&);
};
//目的把cout返回以备后面的用。
ostream& operator<< (ostream&os,const Complex& c){
os<<'('<<c.m_r<<'+'<<c.m_i<<"i)";
return os;
}
int main(){
Complex c(1,2);
cout<<c<<endl<<endl;
}
四.单目操作符重载
#0 //0.operator#()
//operator#(0);
//////////////////////////////////
class Complex{
public :
Complex();
//单目运算符
const Complex operator-(void) const{
return complex(-m_r,-m_i);
}
private:
int m_r;
int m_i;
friend ostream& operator<<(ostream& os,const Complex&c){
return os<<'('<<c.m_r;
}
};
int main(){
complex(10,20);
cout<<-c<<endl;//c.operator-()
}
五.自增减操作符重载
前缀: Complex c(0
++c //c.operator++()
表达式的值是自增减以后的值。可以连用。++++c;
后缀:Complex c();
c++;//c.operator++(0) //哑元区分
表达式的值是自增减以前的值。不可连用。c++++编译错误
/////////////////////////////////
class Complex{
public:
//操作符重载返回自引用,支持连用
Complex& operator++(void){
m_r++;
m_i++;
return *this;
}
//后加加,不能被连用返回常对象
const Complex operator++(int){
Complex old=*this;
m_r++;
m_i++;
return old;
}
private:
int m_r;
int m_i;
friend ostream& operator<< (ostream&os,const Complex& c){
os<<'('<<c.m_r<<'+'<<c.m_i<<"i)";
return os;
}
};
int main(){
Complex c(10,20);
cout<<++c<<endl;
}
并不是所有运算符都可以重载,有些运算符是不能重载的。
六.不能重载的操作符
:: 作用域限定符
. 成员访问
.* 成员指针解引用
?: 三目运算符
sizeof()--获取字节数
typeid()获取类型信息
不能通过操作符重载发明新的操作符,只能把现有的操作符重载。操作符重载尽可能不要改变操作符的原始语义。
练习:实现3*3的矩阵类,实现如下操作符:
+/+=/*/*=/前后++/<<
// 练习:3X3矩阵,支持如下操作符
// +/+=/*/*=/前后++/<<
#include <iostream>
#include <iomanip>
using namespace std;
class M33 {
public:
M33 (int (*a)[3]/* int a[][3] */) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
m_a[i][j] = a[i][j];
}
const M33 operator+ (const M33& m) const {
int a[3][3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
a[i][j] = m_a[i][j] + m.m_a[i][j];
return a;
}
M33& operator+= (const M33& m) {
return *this = *this + m;
}
const M33 operator* (const M33& m) const {
int a[3][3] = {0};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
a[i][j] += m_a[i][k] * m.m_a[k][j];
return a;
}
M33& operator*= (const M33& m) {
return *this = *this * m;
}
M33& operator++ (void) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
m_a[i][j]++;
return *this;
}
const M33 operator++ (int) {
M33 old = *this;
++*this;
return old;
}
friend ostream& operator<< (ostream& os, const M33& m) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
os << setw (4) << m.m_a[i][j];
os << endl;
}
return os;
}
private:
int m_a[3][3];
};
int main (void) {
int a1[3][3] = {1,2,3,4,5,6,7,8,9};
M33 m1 (a1);
cout << "<<:" << endl;
cout << m1 << endl;
int a2[3][3] = {9,8,7,6,5,4,3,2,1};
M33 m2 (a2);
cout << "+:" << endl;
cout << m1 + m2 << endl;
M33 m3 (m1);
cout << "+=:" << endl;
cout << (m3 += m2) << endl;
cout << "*:" << endl;
cout << m1 * m2 << endl;
m3 = m1;
cout << "*=:" << endl;
cout << (m3 *= m2) << endl;
m3 = m1;
cout << "前++:" << endl;
cout << ++m3 << endl;
cout << m3 << endl;
m3 = m1;
cout << "后++:" << endl;
cout << m3++ << endl;
cout << m3 << endl;
return 0;
}
1.与属于对象的非静态成员不同,类的静态成员是属于类的,而不属于对象。
1)静态成员变量的定义和初始化,只能在类的外部进行,而不能够在构造函数(创造对象)中进行。所有在class里面的都只是声明。
2)静态成员变量被该类的多个对象实例所共享。
2.静态成员函数只能访问静态成员,不能访问非静态成员。而非静态成员函数既可以访问非静态成员,也可以访问静态成员。
4)访问静态成员既可以通过类也可以通过对象,但是最好用类。
5)静态成员和非静态成员一样也受类的作用域和访问控制的限制。
///////////////////////////////////
#include<iostream>
using namespace std;
class Account{
public:
Account(const string& name,double balance=0):m_name(name),m_balance(balance){}
void print(void) const{
cout<<m_name<<", "<<m_balance<<endl;
}
void save(double money){
m_balance+=money;
}
bool withdray(double money){
if(money>m_balance)
return false;
m_balance-=money;
return true;
}
void settle(void){
m_balance_=(1+m_rate);
}
//静态成员函数
static void adjust(double rate){
m_rate=rate;//只能访问静态成员
}
private:
string m_name;
double m_balance;
//静态成员变量
static double m_rate;
};
//在类的外部定义并初始化,不能在声明的时候初始化,访问控制不限制定义只限制赋值,运算等。
double Account::m_rate=0.01;
int main(void){
Account acc("张飞");
acc.print();
acc.save(10000);
if(!acc.withdray(5000))
cout<<"取款失败!"<<endl;
else
acc.print();
//静态成员函数调用
Account::m_rate=0.02;
acc.m_rate=0.02;
acc.adjust(0.02);
return 0;
}
//////////////////////////////////
2.单例模式
希望一个类只能创建出一个对象来,创建不出第二个实例来。
1.通过构造函数私有化,禁止在类外部自由创建对象。
2. 通过静态成员控制对象的创建。
3.通过引用计数控制对象的销毁。
#include<iostream>
using namespace std;
class Singleton{
//静态成员函数用来调用构造函数
static Singleton* Create(){
if(!m_inst)
m_inst=new Singleton;
return m_inst;
}
void Destroy(void){
delete this;
}
//构造函数私有化
private:
Singleton(void){
}
//拷贝构造也私有化
Singleton(const Singleton& that){}
static Singleton* m_inst;
~Singleton(void){
m_inst=NULL;
}
};
singleton* Singleton::m_inst=NULL;
int main(){
Singleton * ps1=Singleton::Create();
cout<<(void*)ps1<<endl;
Singleton * ps2=Singleton::Create();
cout<<(void*)ps2<<endl;
}
六。类的成员指针
Student student();
string *pstr=&student.m_name;
pstr不是成员指针。
1.指向成员变量的指针
定义:成员变量的类型 类名::*指针变量名。
String Student::*pstr;
赋值:指针变量名=&类名::成员变量名。
pstr=&Student::m_name;
解引用:对象.*指针变量名,对象指针->*指针变量名。
cout<<student.*pstr;
cout<<ps->*pstr;
实际上,成员变量指针是类中成员变量在类中的偏移量。所以不同的对象调用成员指针可以显示不同对象中的内容。
2.指向成员函数的指针
定义:成员函数返回类型(类名::*指针变量名)(形参表);
void (Student::*pfunc)(void);
赋值:指针变量名=&类名::成员函数名。
pfunc=&Student::who;
解引用:(对象.*指针变量名)(实参表).(对象指针->*指针变量名)(实参表)
(s1.*pfunc)();
(ps->pfunc)();
3.指向静态成员的指针与普通指针没有区别,不需要特殊的语法。
////////////////////////////////
#include<iostream>
using namespace std;
class Student{
public:
Student(const string& name=" ",int age=0):m_name(name),m_age(age){}
void who(){
}
string m_name;
int m_age;
};
int main(void){
string Student::*pstr=&Student::m_name;
int Student::*pn=&Student::m_age;
Student s1("张飞",28);
cout<<s1.*pstr<<','<<s1.*pn<<endl;
Student* ps=new Student("赵云",25);
cout<<ps->*pstr<<','<<ps->*pn<<endl;
delete ps;
//成员函数指针
void (Student::*pfunc)(void) const=&Student::who;
(s1.*pfunc)();
}
第四课 操作符重载
一. 操作符与操作符函数
对于表达式L#R,如果L和R中至少有一个是类类型的对象,那么编译器就会将以上表达式处理为如下函数调用:
L.operator#(R); //成员函数
operator#(L,R); //全局函数
而该函数的返回值就是表达式的值。
二.双目操作符重载
复数:(3+2i)+(4+5i)=7+7i
用复数类表达复数,可以通过重载运算符的方法,实现复数相加的规则。
////////////////////////////////
#include<iostream>
using namespace std;
class Complex{
public:
Complex(int r=0,int i=0):m_r(r),m_i(i){}
void print() const{
cout<<'('<<m_r<<'+'<<m_i<<"i)"<<endl;
}
///重载运算符函数
//使用引用避免拷贝构造函数
//一个非常量引用不能引用常量对旬
//后面的const的目的是兼顾调用这个函数的是一个常量对象c1,即左操作数,形参表里面的const是兼顾c2即右操作数是一个常量的情况。最前面那个const是为了防止将返回值作为左值,一个常量对象是不能被赋值的。
const Complex operator+ ( const Complex& c) const{
complex sum;
sum.m_r=m_r+c.m_r;
sum.m_i=m_i+c.m_i;
return sum;
//return Complex(m_r+c.m_r,m_i+c.m_i);
}
private:
int m_r;//实部
int m_i;//虚部
};
int main(){
//如果c1是常量
Complex c1(1,2);
c1.print();
///c2如果是常量
Complex c2(3,4);
//重载加法运算符
Complex c3=c1+c2; //c3=c1.operator+(c2)
///////////////
Complex ca(10,10),cb(20,20),cc(30,30);
(ca+cb)=cc;//编译不会出错,但是不正确。让其编译出错。让其返回类型变成const
return 0;
}
返回类型为const:防止将函数的返回值用作左值
形参类型为const:接受常量型的右操作数
函数类型为const:处理常量型的左操作数
Complex& operator+=(const Complex& c){
m_r+=c.m_r;
m_i+=c.m_i;
return *this; //返回逢引用的目的是为了让它可以做左值被赋值。
}
c5+=c2;
+=运算是可以做左值的。
Complex ca(10,10),cb(20,20),cc(30,30);
(ca+=cb)=cc;
三.输入输出操作符重载
Complex a();
cout<<a; //cout.operator<<(a);
//operator<<(cout,a);
cin>>a;
///////////////////////////////////
把下面这个函数声明为复数类的友元,那么这个函数便可以访问复数类的私有成员。
class Complex{
public:
complex(int r,int i):m_r(r),m_i(i){}
private:
int m_r;
int m_i;
//以下函数是Complex类的友元
friend ostream& operator<< (ostream&,const Complex&);
};
//目的把cout返回以备后面的用。
ostream& operator<< (ostream&os,const Complex& c){
os<<'('<<c.m_r<<'+'<<c.m_i<<"i)";
return os;
}
int main(){
Complex c(1,2);
cout<<c<<endl<<endl;
}
四.单目操作符重载
#0 //0.operator#()
//operator#(0);
//////////////////////////////////
class Complex{
public :
Complex();
//单目运算符
const Complex operator-(void) const{
return complex(-m_r,-m_i);
}
private:
int m_r;
int m_i;
friend ostream& operator<<(ostream& os,const Complex&c){
return os<<'('<<c.m_r;
}
};
int main(){
complex(10,20);
cout<<-c<<endl;//c.operator-()
}
五.自增减操作符重载
前缀: Complex c(0
++c //c.operator++()
表达式的值是自增减以后的值。可以连用。++++c;
后缀:Complex c();
c++;//c.operator++(0) //哑元区分
表达式的值是自增减以前的值。不可连用。c++++编译错误
/////////////////////////////////
class Complex{
public:
//操作符重载返回自引用,支持连用
Complex& operator++(void){
m_r++;
m_i++;
return *this;
}
//后加加,不能被连用返回常对象
const Complex operator++(int){
Complex old=*this;
m_r++;
m_i++;
return old;
}
private:
int m_r;
int m_i;
friend ostream& operator<< (ostream&os,const Complex& c){
os<<'('<<c.m_r<<'+'<<c.m_i<<"i)";
return os;
}
};
int main(){
Complex c(10,20);
cout<<++c<<endl;
}
并不是所有运算符都可以重载,有些运算符是不能重载的。
六.不能重载的操作符
:: 作用域限定符
. 成员访问
.* 成员指针解引用
?: 三目运算符
sizeof()--获取字节数
typeid()获取类型信息
不能通过操作符重载发明新的操作符,只能把现有的操作符重载。操作符重载尽可能不要改变操作符的原始语义。
练习:实现3*3的矩阵类,实现如下操作符:
+/+=/*/*=/前后++/<<
// 练习:3X3矩阵,支持如下操作符
// +/+=/*/*=/前后++/<<
#include <iostream>
#include <iomanip>
using namespace std;
class M33 {
public:
M33 (int (*a)[3]/* int a[][3] */) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
m_a[i][j] = a[i][j];
}
const M33 operator+ (const M33& m) const {
int a[3][3];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
a[i][j] = m_a[i][j] + m.m_a[i][j];
return a;
}
M33& operator+= (const M33& m) {
return *this = *this + m;
}
const M33 operator* (const M33& m) const {
int a[3][3] = {0};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
for (int k = 0; k < 3; k++)
a[i][j] += m_a[i][k] * m.m_a[k][j];
return a;
}
M33& operator*= (const M33& m) {
return *this = *this * m;
}
M33& operator++ (void) {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
m_a[i][j]++;
return *this;
}
const M33 operator++ (int) {
M33 old = *this;
++*this;
return old;
}
friend ostream& operator<< (ostream& os, const M33& m) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
os << setw (4) << m.m_a[i][j];
os << endl;
}
return os;
}
private:
int m_a[3][3];
};
int main (void) {
int a1[3][3] = {1,2,3,4,5,6,7,8,9};
M33 m1 (a1);
cout << "<<:" << endl;
cout << m1 << endl;
int a2[3][3] = {9,8,7,6,5,4,3,2,1};
M33 m2 (a2);
cout << "+:" << endl;
cout << m1 + m2 << endl;
M33 m3 (m1);
cout << "+=:" << endl;
cout << (m3 += m2) << endl;
cout << "*:" << endl;
cout << m1 * m2 << endl;
m3 = m1;
cout << "*=:" << endl;
cout << (m3 *= m2) << endl;
m3 = m1;
cout << "前++:" << endl;
cout << ++m3 << endl;
cout << m3 << endl;
m3 = m1;
cout << "后++:" << endl;
cout << m3++ << endl;
cout << m3 << endl;
return 0;
}