类和对象(提高)

类和对象(提高)

1、定义一个类 关键字class

6 class Data1
7 {
8 //类中 默认为私有
9 private:
10 int a;//不要给类中成员 初始化
11 protected://保护
12 int b;
13 public://公共
14 int c;
15 //在类的内部 不存在权限之分
16 void showData(void)
17 {
18 cout<<a<<" "<<b<<" "<<c<<endl;
19 }
20 };

2、成员函数在类外实现

class Student
{
private:
    int age;
public:
    void setAge(int a);
    void getAge(void);
};


void Student::setAge(int a)
{
    age=a;
}

void Student::getAge()
{
    cout<<age<<endl;
}

3、类在其他文件实现

头文件定义类, cpp实现类的成员函数

eg:student.h中

#ifndef STUDENT_H
#define STUDENT_H


class Student
{
private:
    int age;
public:
    void setAge(int a);
    void getAge(void);
};

#endif // STUDENT_H

student.cpp中

#include "student.h" //引入头文件
#include<iostream>
using namespace std;


void Student::setAge(int a)
{
    age=a;
}

void Student::getAge()
{
    cout<<age<<endl;
}

main.cpp实现student类

#include <iostream>
#include <cstring>
#include"student.h"
using namespace std;


int main(){
Student stu1;
stu1.setAge(18);
stu1.getAge();

return 0;
}

构造函数

1、如果用户不提供构造函数 编译器会自动 提供一个无参的空的构造函数。

2、如果用户提供构造函数 编译器会自动 屏蔽默认无参的构造

 class Data
 {
int mA;
 public:
Data()
 {
 mA = 0;
 cout<<"无参构造函数"<<endl;
}
 Data(int a)
{
 mA = a;
 cout<<"有参构造函数"<<endl;
 }
};

void test01()
 {
 //隐式调用无参构造(推荐)
 Data ob1;

 //显示调用无参构造
 Data ob2=Data();

 //隐式调用有参构造(推荐)
 Data ob3(10);

 //显示调用有参构造
 Data ob4=Data(20);

 //如果构造函数只有一个参数 可能发生 构造函数的隐式转换(知道就行)
 Data ob5 = 30;//Data ob5(30);

 //匿名对象:当前语句结束 对象就要被释放
 Data();//调用无参
 Data(40);//调用有参

 cout<<ob4.mA<<endl;
 }

析构函数

当对象生命周期结束的时候 系统自动调用析构函数。

函数名和类名称相同,在函数名前加~,没有返回值类型,没有函数形参。(不能被重载)

先调用析构函数 再释放对象的空间。

class Data1
 {
 public:
   int mA;
 public:
 //无参构造函数
 Data1()
 {
 mA=0;
 cout<<"无参构造函数"<<endl;
 }


 //有参构造函数
 Data1(int a)
 {
 mA=a;
 cout<<"有参构造函数 mA="<<mA<<endl;
 }

 //析构函数
 ~Data1()
 {
 cout<<"析构函数 mA="<<mA<<endl;
 }
 };

一般情况下,空的析构函数就足够。但是如果一个类有指针成员,这个类必须 写析构函数,释放指针成员所指向空间。

#include <iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

class Data1{
private:
    char *name;

public:
    Data1(char *str){
        name=(char *)calloc(1,strlen(str)+1);
        strcpy(name,str);
    }
    //释放指针空间
    ~Data1(){
        cout<<"析构函数 name="<<name<<endl;
        if(name!=NULL){
            free(name);
            name=NULL;
        }
    }



};

int main(int argc, char *argv[])
{
Data1("张三");

    return 0;
}

拷贝构造函数

拷贝构造:本质是构造函数

拷贝构造的调用时机:旧对象 初始化 新对象 才会调用拷贝构造。

#include <iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

class Data1{



public:
    int numA;
//拷贝函数
Data1(const Data1 &ob){//ob就是旧对象的别名

    numA=ob.numA;//一旦实现 了拷贝构造 必须完成赋值动作
    cout<<"调用拷贝函数"<<endl;
}
Data1(int a){

    numA=a;
}

};

int main(int argc, char *argv[])
{
Data1 ob1=Data1(10);
Data1 ob2=ob1;
cout<<"ob2:numA="<<ob2.numA<<endl;

    return 0;
}

如果用户不提供拷贝构造 编译器会自动提供一个默认的拷贝构造(完成赋值动作--浅拷贝)

拷贝构造 和 无参构造 有参构造的关系:

如果用户定义了 拷贝构造或者有参构造 都会屏蔽 无参构造。

如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造

拷贝构造函数几种调用方式

1、旧对象给新对象初始化,调用拷贝构造

Data4 ob1(10);
Data4 ob2 = ob1;//调用拷贝构造

2、给对象取别名 不会调用拷贝构造

Data4 ob1(10);
Data4 &ob2 = ob1;//不会调用拷贝构造

3、普通对象作为函数参数 调用喊谁时 会发生拷贝构造

void fun1(Data1 ob){//Data1 ob=obA


    cout<<ob.numA<<endl;
}

int main(int argc, char *argv[])
{

    Data1 obA(10);
    fun1(obA);
    return 0;
}

4、函数返回值普通对象 (Visual Studio会发生拷贝构造)(Qtcreater,linux不会发生)Visual Studio会发生拷贝构造

深拷贝

1、默认的拷贝构造 都是浅拷贝。

2、如果类中没有指针成员, 不用实现拷贝构造和析构函数。

3、如果类中有指针成员, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作。

#include <iostream>
#include<string.h>
#include<stdlib.h>
using namespace std;

class Data1{


public:
    char *name;

//有参构造
Data1(char *n){
     name=(char *)calloc(1,strlen(n)+1);
     strcpy(name,n);
}

~Data1(){
    cout<<"析构函数 name="<<name<<endl;
if(name!=NULL){
    free(name);
    name=NULL;
}

}
//深拷贝
Data1(const Data1 &ob){
name=(char *)calloc(1,strlen(ob.name)+1);
strcpy(name,ob.name);
}


};


int main(int argc, char *argv[])
{

Data1 obA("hello world");
Data1 ob=obA;
    return 0;
}

初始化列表

成员对象:一个类的对象作为另一个类的成员

如果类中想调用成员对象的有参构造 必须使用初始化列表。

class DataA{
public :
    int a;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
    DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
    ~DataA(){
        cout<<"A的析构函数"<<endl;
    }
};


class DataB{
public:
    int b;
    DataA ob;
public:
    DataB(){
        cout<<"B的无参构造"<<endl;
    }
    DataB(int num,int numa)
    {
        b=num;
        ob.a=numa;
        cout<<"B的有参构造"<<endl;
    }
    ~DataB(){
        cout<<"B的析构函数"<<endl;
    }
};

int main(int argc, char *argv[])
{
    DataB ob(10,20);

    return 0;
}

打印结果:

A的无参构造
B的有参构造
B的析构函数
A的析构函数

类会自动调用成员对象的无参构造。

类想调用成员对象 有参构造 必须使用初始化列表。

使用初始化列表,调用成员对象的有参构造

   DataB(int num,int numa):ob(numa)//修改B的有参构造
    {
        b=num;
        cout<<"B的有参构造"<<endl;
    }

A的有参构造
B的有参构造
B的析构函数
A的析构函数

对象数组

对象数组:本质是数组 数组的每个元素是对象

class DataA{
public :
    int a;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
    DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
    ~DataA(){
        cout<<"A的析构函数"<<endl;
    }
};



int main(int argc, char *argv[])
{
    //对象数组 每个元素都会自动调用构造和析构函数
    //对象数组不会自动初始化 每个元素 调用无参构造
//   ataA array[5];

    //对象数组的初始化 必须显示使用有参构造 逐个元素初始化
    DataA array[5]={DataA(1),DataA(2),DataA(3),DataA(4),DataA(5)};
    int len=sizeof(array)/sizeof(array[0]);
    for(int i=0;i<len;i++){
        cout<<array[i].a<<" ";
    }
    cout<<endl;
    return 0;
}

A的有参构造
A的有参构造
A的有参构造
A的有参构造
A的有参构造
1 2 3 4 5
A的析构函数
A的析构函数
A的析构函数
A的析构函数
A的析构函数

explicit函数

explicit防止构造函数隐式转换

什么是隐式转换?

class DataA{
public :
    int a;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
    DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
    ~DataA(){
        cout<<"A的析构函数"<<endl;
    }
};



int main(int argc, char *argv[])
{

    DataA ob=10;//隐式转换 DataA ob=Data(10);
    
    return 0;
}

加上explicit关键字防止隐式转换【new和delete 堆区空间操作

class DataA{
public :
    int a;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
    explicit DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
    ~DataA(){
        cout<<"A的析构函数"<<endl;
    }
};



int main(int argc, char *argv[])
{

    DataA ob=10;//error
    return 0;
}

new和delete 堆区空间操作

1、new和delete操作基本类型的空间

int main(int argc, char *argv[])
{
    int *p=new int(5);

    cout<<"*p="<<*p<<endl;
    delete p;
    return 0;
}

区别:

new 不用强制类型转换(malloc返回的类型是(void *))

new在申请空间的时候可以 初始化空间内容

new 申请基本类型的数组

int main(int argc, char *argv[])
{
    int *p=new int[5]{1,2,3,4,5};
    for(int i=0;i<5;i++){
        cout<<p[i]<<endl;
    }
    delete []p;//delete 数组要加上[]
    return 0;
}

2、new和delete操作类

malloc不会调用构造函数 free不会调用析构函数

new 会调用构造函数 delete调用析构函数

class DataA{
public :
    int a;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
    explicit DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
    ~DataA(){
        cout<<"A的析构函数"<<endl;
    }
};


int main(int argc, char *argv[])
{
DataA *p1=new DataA();
DataA *p2=new DataA();
DataA *array=new DataA[5]{DataA(1),DataA(2),DataA(3),DataA(4),DataA(5)};

delete p1;//delete
delete p2;
delete[]array;//释放数组的空间
    return 0;
}

静态成员

类的对象 拥有独立的 普通成员数据。

static 修饰的成员 叫 静态成员

class Data
 {
 static int a;//静态成员数据
 static void func()//静态成员函数
 {

 }

①静态成员数据

static修饰的静态成员 属于类而不是对象。(所有对象共享 一份 静态成员数据)

static修饰的成员 定义类的时候 必须分配空间。

static修饰的静态成员数据 必须类中定义 类外初始化。

class DataA{
public :
    int a;
    static int b;
public:
    DataA(){
        cout<<"A的无参构造"<<endl;
    }
     DataA(int num){
        a=num;
        cout<<"A的有参构造"<<endl;
    }
};

int DataA::b=100;//类外初始化

int main(int argc, char *argv[])
{

   cout<<DataA::b<<endl;//100
   DataA *ob=new DataA();
   cout<<ob.b<<endl;//100
   ob.b=200;
     cout<<DataA::b<<endl;//200
    return 0;
}

静态成员数据统计对象个数

#include <iostream>

using namespace std;

class DataA{
public :
    int a;
    static int count;
public:
    //无参构造
    DataA(){
        count++;
    }
    //有参构造
    DataA(int num){
        a=num;
    count++;
    }
     //拷贝构造
     DataA(const DataA &ob){
         a=ob.a;
         count++;
     }
     //析构函数
     ~DataA(){
         count--;
     }
};

int DataA::count=0;//类外初始化

int main(int argc, char *argv[])
{
DataA oba;
DataA obb(10);
DataA obc=oba;
cout<<"对象个数为"<<DataA::count<<endl;
    return 0;
}

静态成员函数

静态成员函数 是属于类 而不是对象(所有对象共享)

1、静态成员函数可以直接通过类名称访问

2、静态成员函数内只能操作静态成员数据

单例模式

单例模式的类,只能实例化一个对象

1、构造函数私有化

2、定义一个私有的对象指针,保存唯一的实例地址

#include <iostream>

using namespace std;

class SingleTon{

    //构造函数私有化
private:
     SingleTon(){
       count=0;
       cout<<"构造"<<endl;
    }
     ~SingleTon(){
         cout<<"析构函数"<<endl;
     }
     SingleTon(const SingleTon &ob){
         count=0;
         cout<<"拷贝构造"<<endl;
     }
 private:
     static SingleTon *const p;
     int count;

public:
     static SingleTon *getSingleTon(void){

         return p;
     }
     void printStirng(char *str){
         count++;
         cout<<"当前第"<<count<<"次任务打印:"<<str<<endl;
     }

};
SingleTon *const SingleTon::p=new SingleTon;


int main(int argc, char *argv[])
{
  SingleTon *p=SingleTon::getSingleTon();
  p->printStirng("hello");
  
    SingleTon *p2=SingleTon::getSingleTon();
      p2->printStirng("hello");
    return 0;
}

类的存储结构

类的成员函数和静态变量不占类的内存空间

const修改成员函数为只读

#include <iostream>
#include<string>
#include<stdlib.h>
using namespace std;

class DataA{
public :
    int a;
    mutable int b;//mutable修饰的成员变量可以修改

public:
  void printString(void) const{//const修饰成员函数为只读
//      this->a=100;//在打印函数中修改成员变量   error

      cout<<"a="<<a<<endl;
      this->b=100;
      cout<<"b="<<b<<endl;
  }
  DataA(int a,int b){
      this->a=a;

      this->b=b;
  }
};


int main(int argc, char *argv[])
{
    DataA ob=DataA(10,20);
    ob.printString();
  return 0;
}

友元

1、普通全局函数作为类的友元

#include <iostream>
#include<string>

using namespace std;

class Room{
    friend void visit(Room &room);//friend关键字
private:
    string bedRoom;//卧室
public:
    string setingRoom;//客厅
public:
    //构造函数
    Room(string bedRoom,string setingRoom){
        this->bedRoom=bedRoom;
        this->setingRoom=setingRoom;
    }

};

//普通的全局函数
void visit(Room &room){
    cout<<"访问了"<<room.setingRoom<<endl;
    cout<<"访问了"<<room.bedRoom<<endl;
}


int main(int argc, char *argv[])
{
    Room room("张三卧室","张三客厅");
    visit(room);
  return 0;
}

2、类的某个成员函数 作为另一个类的友元

#include <iostream>
#include<string.h>

using namespace std;

class Room;//先声明


class goodGay{
public:
    void visit01(Room &room);//先在类中声明,类外实现(否则之前只声明了Room,并没有声明Room中的成员,所以访问不了)
    void visit02(Room &room);
};

class Room{

friend  void goodGay::visit02(Room &room);//将类中成员函数声明为友元
private:
    string bedRoom;//卧室
public:
    string setingRoom;//客厅
public:
    //构造函数
    Room(string bedRoom,string setingRoom){
        this->bedRoom=bedRoom;
        this->setingRoom=setingRoom;
    }

};

//类外实现
void goodGay::visit01(Room &room)
 {
 cout<<"李四访问了"<<room.setingRoom<<endl;

 }

 void goodGay::visit02(Room &room)
 {
 cout<<"好基友张八访问了"<<room.setingRoom<<endl;
 cout<<"好基友张八访问了"<<room.bedRoom<<endl;
 }



int main(int argc, char *argv[])
{
    Room room("张三卧室","张三客厅");
  goodGay ob;
  ob.visit01(room);
  ob.visit02(room);
  return 0;
}

3、 整个类作为友元

这个类的所有成员函数 都可以访问另一个类的私有数据

#include <iostream>
#include<string.h>

using namespace std;

class Room;//先声明


class goodGay{
public:
    void visit01(Room &room);//先在类中声明,类外实现(否则之前只声明了Room,并没有声明Room中的成员,所以访问不了)
    void visit02(Room &room);
};

class Room{

    friend class goodGay;

private:
    string bedRoom;//卧室
public:
    string setingRoom;//客厅
public:
    //构造函数
    Room(string bedRoom,string setingRoom){
        this->bedRoom=bedRoom;
        this->setingRoom=setingRoom;
    }

};


void goodGay::visit01(Room &room)
 {
 cout<<"李四访问了"<<room.setingRoom<<endl;
 }


 void goodGay::visit02(Room &room)
 {
 cout<<"好基友张八访问了"<<room.setingRoom<<endl;
 cout<<"好基友张八访问了"<<room.bedRoom<<endl;
 }

int main(int argc, char *argv[])
{
    Room room("张三卧室","张三客厅");
  goodGay ob;
  ob.visit01(room);
  ob.visit02(room);
  return 0;
}

注意

1.友元关系不能被继承。

2.友元关系是单向的,类A是类B的朋友,但类B不一定是类A的朋友。

3.友元关系不具有传递性。类B是类A的朋友,类C是类B的朋友,但类C不一定是类A的朋友

设计动态数组类

Array.h

#ifndef ARRAY_H
#define ARRAY_H
class Array{
private:
    int *arr;//存放首地址元素
    int capacity;//容量
    int size;//大小

public:
    Array();
    Array(int capacity);
    Array(const Array &ob);
    ~Array();

    int getCapacity() const;//get容量
    int getSize() const;//getSize
    void printArray(void);//打印数组
    void pushBack(int elem);//从尾部插入数据
    void popBack(void);//删除尾部元素
    int &at(int pos);//获取元素,并返回引用类型
   };



#endif // ARRAY_H

array.cpp

#include "array.h"
#include "string.h"
#include <iostream>
using namespace std;
Array::Array(){
    capacity=5;
    size=0;
    arr=new int[capacity];
    memset(arr,0,sizeof(int)*capacity);

}
Array::Array(const Array &ob){
    capacity=ob.capacity;
    size=ob.size;
    arr=new int[capacity];
    memcpy(arr,ob.arr,sizeof(int)*capacity);

}
Array::~Array(){
    if(arr!=NULL){
        delete[]arr;
        arr=NULL;
    }
}
int Array::getCapacity()const{
    return capacity;
}
int Array::getSize()const{
    return size;
}
void Array::pushBack(int elem){
    if(size==capacity){
        int *temp=new int[2*capacity];
        memcpy(temp,arr,sizeof(int)*capacity);
        delete[]arr;
        arr=temp;
        capacity=2*capacity;

    }
    arr[size++]=elem;
    return;
}
void Array::popBack(){
    if(size==0){
        cout<<"容量为空"<<endl;

    }
    else{
        size--;
    }
    return;
}
int &Array::at(int pos){

    if(pos<0||pos>=size){
        cout<<"访问违法内存"<<endl;
        exit(-1);
    }
    return arr[pos];
}
void Array::printArray(void){
    for(int i=0;i<size;i++){
        cout<<arr[i]<<" ";

    }
    cout<<endl;
}

main.cpp

#include "array.h"
#include <iostream>
using namespace std;


int main(int argc, char *argv[])
{
Array array;
array.pushBack(10);
array.pushBack(20);
array.pushBack(30);
array.pushBack(40);
array.printArray();
array.at(2)=100;
array.printArray();
  return 0;
}
相关推荐
不会写代码的ys5 分钟前
【类与对象】--对象之舞,类之华章,共绘C++之美
c++
兵哥工控7 分钟前
MFC工控项目实例三十二模拟量校正值添加修改删除
c++·mfc
长弓聊编程17 分钟前
Linux系统使用valgrind分析C++程序内存资源使用情况
linux·c++
cherub.24 分钟前
深入解析信号量:定义与环形队列生产消费模型剖析
linux·c++
暮色_年华38 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子1 小时前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
我们的五年1 小时前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
做人不要太理性2 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.2 小时前
2、桥接模式
c++·桥接模式
chnming19872 小时前
STL关联式容器之map
开发语言·c++