第二章 微观部分:面向对象之类的组成
- 类
- 函数
- 构造函数
- 析构函数
- 拷贝构造函数
- 运算符重载函数
- ==封装一个字符串类==
- 初始化列表
- this指针
- 常对象和常成员函数(方法)
- 静态属性和静态成员函数
- ==单例设计模式==
类
对象:属性和方法组成,是类的一个实例化
类(class):具有相同属性和方法,是对象的抽象
类的封装性:权限(public/private/protected)+属性+方法
类的大小(和结构体相似):
空类,1byte;类对象的大小与类中的非静态属性相关,类对象共享类中的成员函数的地址
练习:
#include <iostream>
using namespace std;
class rectangle{
int wide;
int length;
public:
void setwide(int wide){
this->wide=wide;
}
void setlength(int length){
this->length=length;
}
int getArea(){
return this->wide*this->length;
}
};
rectangle& compair(rectangle& r1,rectangle& r2){
return r1.getArea()>r2.getArea()?r1:r2;
}
int main()
{
int wide=10;
int length=20;
rectangle *rect1=new rectangle();
rect1->setwide(wide);
rect1->setlength(length);
cout << "rectangle's area1 = " << rect1->getArea() << endl;
wide=20;
length=30;
rectangle rect2;
rect2.setwide(wide);
rect2.setlength(length);
cout << "rectangle's area2 = " << rect2.getArea() << endl;
cout << "---------------------------------------------------" << endl;
cout << "较大的面积为:" << endl;
cout << compair(*rect1,rect2).getArea() << endl;
return 0;
}
函数
类中函数(类的骨架):
- 构造函数
- 析构函数
- 可被构造函数
- 运算符重载函数
- 取值运算符
- 长取值运算符
构造函数
没有任何返回值,函数名与类名相同,由编译器根据对象定义参数决定
- 不存在,编译器生成一个无参的空构造
- 存在,编译器不在生成
- 意义:对类对象属性进行初始化,
类中get()/set()函数的升级
#include <iostream>
using namespace std;
class stu{
string name;
int age;
public:
stu(){
cout << "stu的无参构造" << endl;
}
stu(string name,int age){
this->name=name;
this->age=age;
cout << "stu的有参构造" << endl;
}
void showInfo(){
cout << "姓名:" << this->name << " 年龄:" << this->age << endl;
}
};
int main()
{
stu s1;
// stu s1();//是一个函数声明,并非是无参函数的调用
stu s2("yao",18);
s2.showInfo();
stu *s3=new stu("yaoliang",19);
s3->showInfo();
delete s3;
return 0;
}
显示(explicit)调用和隐式(implicit expression) 调用
#include <iostream>
using namespace std;
class A{
int a;
public:
A(){
cout << "i am is NULL structure" << endl;
}
A(int a){
cout << "i am is single structure" << endl;
}
A(int a,int b){
cout << "i am is more structure" << endl;
}
explicit A(int a,int b,int c){
cout << "i am is more more structure" << endl;
}
};
int main()
{
cout << " explicit structure " << endl ;
A a;
A a1(1);
A a2(1,2);
A a3(1,2,3);
cout << " implicit expression strcture" << endl ;
A a4 = 1;
A a5 = {1,2};
// A a6 = {1,2,3};//在类构造函数前加上explicit,无法隐式调用
return 0;
}
析构函数
#include <iostream>
using namespace std;
class stu{
string name;
int age;
int *p;
public:
stu(string name,int age){
this->name=name;
this->age=age;
this->p=new int[1024];
cout << "stu structure" << endl;
}
~stu(){
delete []p;
cout << "stu destruct" << endl;
}
};
int main()
{
stu s1("zhangsan",10);
stu *s2 = new stu("yao",18);
delete s2;
return 0;
}
好处:避免内存泄漏,先清理资源,在销毁空间
拷贝构造函数
类中没有提供拷贝构造函数,当出现使用其他对象为本对象进行初始化时,编译器自动生成,以供调用
浅拷贝:
- 优点:简单赋值,可以提高效率
- 缺点:指针共享一块内存,析构后,内存被回收
#include <iostream>
using namespace std;
class stu{
string _name;
int _age;
int *_score;
public:
stu(string name,int age,int *score)
{
this->_name=name;
this->_age=age;
this->_score=new int[10] ();
memcpy(this->_score,score,10);
// this->_score=score;
cout << "stu structure" << endl;
}
~stu(){
delete [] _score;
cout << "stu destruct" << endl;
}
stu(const stu& other){
this->_name=other._name;
this->_age=other._age;
// this->_p=other._p;
//深拷贝
this->_score=new int[10];
memmove(this->_score,other._score,sizeof(int[10]));
cout << "stu copy" <<endl;
}
void showInfo()const{
cout << "姓名: " << this->_name ;
cout << "年龄: " << this->_age ;
cout << "成绩: " << this->_score[0] ;
cout << endl;
}
int getAge()const{
return this->_age;
}
};
const stu& compair(const stu& s1,const stu& s2){
return s1.getAge()>s2.getAge()?s2:s1;
}
//多拷贝3次
//stu compair(stu s1,stu s2){
// return s1.getAge()>s2.getAge()?s2:s1;
//}
int main()
{
int score[10]={100,80,40};
//显示调用
stu s1("yaoliang",18,score);
stu s2(s1);
s2.showInfo();
//隐式调用
stu s3("minmin",17,score);
stu s4=s3;
s4.showInfo();
cout << "-----------stu's age compair ,input small age imformation------------" << endl;
compair(s2,s4).showInfo();
return 0;
}
引用可以提高深拷贝的运行效率,使用常引用,保证实参安全
运算符重载函数
面向对象服务,全局也可以定义,但类中优先,存在深浅拷贝(针对=)
等号运算符重载函数,深拷贝要谨慎
stu& operator=(const stu& other){
this->_name=other._name;
this->_age=other._age;
//判空,删除原空间,创建新空间
if(this->_score!=nullptr){
delete []_score;
}
this->_score=new int[10] ();
memcpy(this->_score,other._score,10);
cout << "stu equal operator overloading" <<endl;
return *this;
}
测试代码:
#include <iostream>
using namespace std;
class stu{
string _name;
int _age;
int *_score;
public:
stu(string name,int age,int *score)
{
this->_name=name;
this->_age=age;
this->_score=new int[10] ();
memcpy(this->_score,score,10);
// this->_score=score;
cout << "stu structure" << endl;
}
~stu(){
delete [] _score;
cout << "stu destruct" << endl;
}
stu(const stu& other){
this->_name=other._name;
this->_age=other._age;
// this->_p=other._p;
//深拷贝
this->_score=new int[10];
memmove(this->_score,other._score,sizeof(int[10]));
cout << "stu copy" <<endl;
}
//系统默认
stu& operator=(const stu& other){
this->_name=other._name;
this->_age=other._age;
//要必须小心
if(this->_score!=nullptr){
delete []_score;
}
this->_score=new int[10] ();
memcpy(this->_score,other._score,10);
cout << "stu equal operator overloading" <<endl;
return *this;
}
//自定义加法
int operator+(const stu& other){
return this->getAge()+other.getAge();
}
//自定义减法
int operator-(const stu& other){
cout << "class inside" << endl;
return this->getAge()+other.getAge();
}
void showInfo()const{
cout << "姓名: " << this->_name ;
cout << "年龄: " << this->_age ;
cout << "成绩: " << this->_score[0] ;
cout << endl;
}
int getAge()const{
return this->_age;
}
};
const int operator-(const stu& s1,const stu& s2){
cout << "class external" << endl;
return s1.getAge()-s2.getAge();
}
const stu& compair(const stu& s1,const stu& s2){
return s1.getAge()>s2.getAge()?s2:s1;
}
//stu compair(stu s1,stu s2){
// return s1.getAge()>s2.getAge()?s2:s1;
//}
int main()
{
int score[10]={100,80,40};
//显示调用
stu s1("yaoliang",18,score);
stu s2(s1);
s2.showInfo();
//隐式调用
stu s3("minmin",17,score);
stu s4=s3;
s4.showInfo();
s3=s2;
s3.operator=(s2);
s3.showInfo();//系统自动创建了一个等号运算符重载
cout << "---stu's age compair ,input small age imformation----" << endl;
compair(s2,s4).showInfo();
cout << "--using operator overloading (overall situation) ,stu age sub--" << endl;
cout << s1-s4 << endl;
cout << operator-(s1,s4) << endl;
cout << "-----------using operator overloading ,stu age add------------" << endl;
cout << s1+s4 << endl;
return 0;
}
注意:
- 在全局不可重载的运算符:=,->,[],()
- 不能重载:.(.运算符),::(域运算符),.*(. *运算符),?:(三目运算符),(sizeof运算符),(#预处理运算符)
自增自减运算符
前自增
返回值 operator++()
后自增:
返回值 operator++(int)//亚元
#include <iostream>
#include <unistd.h>
using namespace std;
class Clock{
int min;
int sec;
public:
Clock(int m,int s){
this->min=m;
this->sec=s;
}
Clock& operator++(){
++sec;
if(0==sec%60){
++min;
sec=0;
if(0==min%60){
this->min=0;
}
}
return *this;
}
Clock& operator++(int){
sec++;
if(sec%60==0){
min++;
sec=0;
if(min%60==0){
this->min=0;
}
}
return *this;
}
//Clock& operator++(int){
// this->operator++();
// (*this).operator++();
// return ++*this;//三种效果一致
//}
void display(){
cout << this->min << ":" << this->sec << endl;
}
};
int main()
{
Clock c(0,0);
while(true){
c++;
c.display();
sleep(1);
}
return 0;
}
封装一个字符串类
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
private:
char* m_data;
public:
//MyString无参空构造
MyString(){
this->m_data=new char[1];
this->m_data[0]='\0';
}
//MyString有参构造:在c++中字符串常量的类型,一定是const char* 类型
MyString(const char* c_str){
int len=strlen(c_str);
this->m_data=new char[len+1];
memmove(this->m_data,c_str,len);
this->m_data[len]='\0';
}
//MyString 的拷贝构造
MyString(const MyString& other){
int len=strlen(other.m_data);
this->m_data=new char[len+1];
memmove(this->m_data,other.m_data,len);
this->m_data[len]='\0';
}
//=号运算符重载函数
MyString& operator=(const MyString& other){
if(this==&other){
return *this;
}
int len=strlen(other.m_data);
if(nullptr!=this->m_data){
delete [] m_data;
this->m_data=new char[len+1];
}else{
this->m_data=new char[len+1];
}
memmove(this->m_data,other.m_data,len);
this->m_data[len]='\0';
return *this;
}
//析构
~MyString(){
delete []m_data;
this->m_data=nullptr;
}
//查找下标元素
char operator[](int index){
if(index < 0 || index >= strlen(this->m_data)){
cout << "cross the border" << endl;
}
return this->m_data[index];
}
//加法运算
MyString operator+(const MyString& other){
int my_len=strlen(this->m_data);
int other_len=strlen(other.m_data);
char *temp=new char[my_len+other_len+1];
memmove(temp,this->m_data,my_len);
memmove(temp+my_len,other.m_data,other_len);
temp[my_len+other_len]='\0';
MyString temp_str=temp;
delete [] temp;
return temp_str;
}
char* getM_data()const{
return this->m_data;
}
};
ostream& operator << (ostream& cout,const MyString& other){
cout << other.getM_data();
return cout;
}
int main()
{
MyString str;
MyString str1("hello");
MyString str2=str1;
MyString str3="yao";
MyString str4="liang";
cout << str1.getM_data() << endl;
cout << str1 << endl;
cout << str2 << endl;
cout << str1[2] << endl;
cout << str3+str4 << endl;
return 0;
}
初始化列表
const修饰的常属性(只读属性)的初始化方式。
在C++中const修饰的对象,必要初始化
类中构造函数的特殊语法(高效率):
类中构造函数的初始化列表,早于构造函数,在对象开辟空间时,解决const问题
初始化列表语法:在类中构造函数后+:类中的属性(外部参数)
遵循原则:顺序初始化(忽略static修饰的属性)
#include <iostream>
using namespace std;
class Score{
int score;
public:
Score(int score){
cout << this->score << endl;
}
};
class stu{
string _name;
int _age=128;
int _salary;
string *_friend;
Score _s=Score(100);
public:
stu(string name,int age):_name(name),_age(age),_salary(10000),_friend(new string[10]{"sunsun"}),_s(Score(100))
{
cout << "stu的有参构造" << endl;
}
~stu(){
delete []_friend;
cout << "stu destruct" << endl;
}
void showInfo(){
cout << "姓名:" << this->_name << " 年龄:" << this->_age ;
cout << "工资:" << this->_salary << " 朋友1:" << this->_friend[0] ;
cout << endl;
}
};
int main()
{
stu s1("yaoliang",18);
s1.showInfo();
Score s(10);
return 0;
}
this指针
在c中的体现:
#include <stdio.h>
typedef void (*Pfunc_t)();
typedef struct Stu
{
char *name;
int age;
Pfunc_t f;
}stu;
void showInfo(stu *const this){
printf("姓名:%s,年龄:%d\n",this->name,this->age);
}
int main(int argc, const char *argv[])
{
/*your code*/
stu s={"yaoliang",20,showInfo};
s.f(&s);
return 0;
}
C++中this指针
- 就是本对象的起始地址(const修饰的常地址)
- 是隐藏(非静态)在成员函数最左侧的一个常变量形参
两种用法:
- 区分属性名与形参名
- 解引用,返回本对象
#include <iostream>
using namespace std;
class Stu{
string name;
int age;
public:
//1.当成员函数中形参变量与类属性变量重名时,使用this加以区分
Stu(string name,int age){
this->name=name;
this->age=age;
// name=name;//error
// age=age;//error
}
void showInfo(){
cout << "姓名: " << this->name << " , 年龄: " << this->age << endl;
// cout << "姓名: " << name << " , 年龄: " << age << endl;//error
}
//2.返回本对象 *this
Stu& setName(string name){
this->name=name;
return *this;
}
};
int main()
{
Stu stu("yaoliang",18);
stu.showInfo();
stu.setName("liang").showInfo();
return 0;
}
常对象和常成员函数(方法)
常对象,const修饰的对象
特性:
-
只读,不可修改
-
不能调用普通成员函数(有修改权限),只能调用常函数
常函数,const修饰的函数
特性:
- 不修改函数类中的属性,只读
#include <iostream>
using namespace std;
class rectangle{
int wide;
int length;
public:
// rectangle(int wide,int length){
// this->wide=wide;
// this->length=length;
// }
void setwide(int wide){
this->wide=wide;
}
void setlength(int length){
this->length=length;
}
int getArea()const{
return this->wide*this->length;
}
};
//更安全
const rectangle& compair(const rectangle& r1,const rectangle& r2){
return r1.getArea()>r2.getArea()?r1:r2;
}
//rectangle& compair( rectangle& r1,rectangle& r2){
// r1.setwide(10);
// r1.setlength(100000);
// return r1.getArea()>r2.getArea()?r1:r2;
//}
int main()
{
int wide=10;
int length=20;
rectangle *rect1=new rectangle();
rect1->setwide(wide);
rect1->setlength(length);
cout << "rectangle's area1 = " << rect1->getArea() << endl;
wide=20;
length=30;
rectangle rect2;
rect2.setwide(wide);
rect2.setlength(length);
cout << "rectangle's area2 = " << rect2.getArea() << endl;
cout << "---------------------------------------------------" << endl;
cout << "较大的面积为:" << endl;
cout << compair(*rect1,rect2).getArea() << endl;
return 0;
}
静态属性和静态成员函数
特点:
-
静态属性**资源共享**
-
静态属性隐藏于类域之中,可以通过域名+::(域名访问符)直接访问
-
静态成员函数访问不到类中属性,原因:最左侧没有常变量形参this
-
静态成员函数可以访问静态属性,与静态属性类似,不依赖于某个对象调用
-
static修饰
- 修饰属性时,修饰的是属性的存储形式,需要在类外全局
.cpp
文件中定义:例:stu::count=0;
- 修饰函数时,修饰的是级别,隐藏在本类的类域中,为全类服务,可以域名::方式直接调用
- 修饰属性时,修饰的是属性的存储形式,需要在类外全局
#include <iostream>
using namespace std;
class stu{
string _name;
const int _age=128;
int _salary;
string *_friend;
static int count;
public:
stu(string name,int age):_name(name),_age(age),_salary(10000),_friend(new string[10]{"sunsun"})
{
count++;
cout << "stu的有参构造" << endl;
}
~stu(){
delete []_friend;
cout << "stu destruct" << endl;
}
void showInfo(){
cout << "姓名:" << this->_name << " 年龄:" << this->_age ;
cout << "工资:" << this->_salary << " 朋友1:" << this->_friend[0] ;
cout << endl;
}
static int get_count(){
return count;
}
};
//需要在全局定义的原因:
//静态变量在声明时就需要开辟空间,而在类中不会开辟空间
int stu::count = 0;
int main()
{
stu s1("yaoliang",18);
s1.showInfo();
cout << "count = " << stu::get_count() << endl;//1
return 0;
}
单例设计模式
一个类,在外部只能生成一个实例,最大限度的减小资源占用
以下为**饿汉式**:
- 优点:线程安全
- 缺点:占用一块堆上的资源
#include <iostream>
using namespace std;
class singleton{
private:
//1.把类中的构造设置为私有
singleton(){
cout << "singleton的构造" << endl;
}
static singleton* my_instance;
public:
//2.提供一个公有接口函数返回本对象的指针
//3.公有接口不想依赖类对象的调用,是不是应该把该接口升级为static静态成员函数
static singleton* getInstance(){
//4.在类私有属性中定义一个静态的私有成员对象类型的属性指针,并把公有接口中返回出去
return my_instance;
}
};
//5.在类外完成对类中静态属性指针的初始化
singleton* singleton::my_instance=new singleton();
int main()
{
singleton* s1=singleton::getInstance();
singleton* s2=singleton::getInstance();
singleton* s3=singleton::getInstance();
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
return 0;
}
以下为**懒汉式**:
- 优点:不用不占用资源
- 缺点:
- 生成死递归,所以懒汉不提供析构,只能自己创建
- 不在使用全部申请的空间,线程不安全,所以要加智能锁
#include <iostream>
#include <mutex>
using namespace std;
//设置全局锁
mutex mtx;
class singleton{
private:
//1.把类中的构造设置为私有
singleton(){
cout << "singleton的构造" << endl;
}
static singleton* my_instance;
public:
//2.提供一个公有借口函数返回本对象的指针
//3.公有接口不想依赖类对象的调用,是不是应该把该接口升级为static静态成员函数
static singleton* getInstance(){
//4.在类私有属性中定义一个静态的私有成员对象类型的属性指针,并把公有接口中返回出去
//c++11智能锁类型(可被析构):
lock_guard<mutex> lock(mtx);//c++11线程安全的懒汉式单例
if(nullptr==my_instance){
my_instance =new singleton();
}
return my_instance;
}
//懒汉式析构
void destroy(){
if(nullptr!=my_instance){
delete my_instance;
my_instance=nullptr;
}
}
//析构
// ~singleton(){
delete my_instance;//生成死递归,所以懒汉不提供析构
// }
};
//5.在类外完成对类中静态属性指针的初始化
singleton* singleton::my_instance=nullptr;
int main()
{
singleton* s1=singleton::getInstance();
singleton* s2=singleton::getInstance();
singleton* s3=singleton::getInstance();
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
return 0;
}
注意:类中的静态属性当其初始化时,可以调用类中的私有属性和方法