类和对象(提高)
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;
}