俗话说,面向对象是C++区别于C的基础和核心。本文将简要介绍C++的类与对象。
访问控制属性
public | private | protected |
可以类外访问 | 只能由类成员和友元访问 | 可以被派生类对象访问 |
一部分函数可以是public的 | 数据尽量定义成private的 |
类的实现
- 类的成员函数描述的是类的行为或操作
- 函数的原型声明要在类的主体中,而函数的实现一般写在类声明之外
- 定义类的成员函数:
返回值类型 类名::成员函数名(参数表){
函数体
}
4. 成员函数可以访问本类的任意成员。这表明成员函数可以直接调用类里面已经定义的所有成员,无论是public, private还是protected.
面向对象一般写三个文件,一个是定义类成员函数的头文件(.h),一个是实现函数的.cpp文件,一个是主程序文件。下面举个例子。
这是clock.h中的内容:
//clock.h
class Clock{
public:
void setTime(int NHour, int NMinute, int NSecond);//参数可以带默认值,但注意要在右边
void showTime();
private:
int Hour;
int Minute;
int Second;
};
//clock.cpp
#include "Clock.h"
void Clock::setTime(int NHour, int NMinute, int Nsecond){//setTime()函数
Hour = NHour;
Minute = NMinute;
Second = NSecond;
void Clock::showTime(){
cout << Hour << ":" << Minute << ":" << Second << endl;
}
对象
定义对象和定义一般变量并无不同,为:<类名> <对象名>;例如:
#include "Clock.h"
#include
using namespace std;
int main(){
Clock myclock;
myclock.setTime(12, 5, 0);
myclock.showTime();
}
要注意的是,类中的变量一定是不能有初始值的。因为类是一个抽象的概念,不占用空间。
实例化之后,这个实例的变量会被分配空间,但是函数是不占用空间的。
类的作用域
类作用域是指类定义和相应的成员函数定义的范围,通俗地称为类的内部。
c++认为一个类的全部成员都是一个整体的相关部分。
一个类的所有成员位于这个类的作用域内,在该范围内,一个类的成员函数对本类的其它成员拥有无限制的访问权。
类的可见性
- 类名实际上是个类型名允许与其它变量、函数同名;
- 在类的内部,与类或类的成员同名的全局变量名和函数名将不可见。
构造函数
定义构造函数的一般形式为:
class 类名{
public:
类名(形参表);
}
类名::类名(形参表){//构造函数不允许返回值
函数体;
}
}
构造函数的名字与类名是完全相同的,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。构造函数必须是公有的。
构造函数可以带默认形参,也可以重载。
每一个类都会存在构造函数,如果用户没有写,编译器会自己创造一个;如果写了就不会重新创造了。
实例:构造函数:
class Clock{
public:
Clock(int H, int M, int S);//构造函数
void setTime(int NHour, int NMinute, int NSecond);
void ShowTime();
private:
int Hour;
int Minute;
int Second;
};
Clock::Clock(int H, int M, int S){
Hour = H;
Minute = M;
Second = S;
}
//主程序中:
Clock myClock(12, 0, 1);
myClock.showTime();//正确,实例化中调用构造函数的形参
Clock b;//错误
构造函数的重载:
class Tdate{
public:
Tdate();
Tdate(int d);
Tdate(int m, int d);
Tdate(int y, int m, int d);
private:
int month;
int day;
int year;
}
上述示例可以在传入参数个数不同的情况下重载不同的函数。
复制构造函数
复制构造函数的形参是类的对象的引用
Clock(const Clock &a){......}
复制构造函数与原来的构造函数实现了构造函数的重载
自动生成:如果在类定义的时候没有定义复制构造函数,编译器会生成默认的构造函数
何时会执行复制构造函数:
- 通过“=”复制对象时
- 对象作为函数的参数,函数调用时
- 函数返回对象时
复制构造函数是否可有可无?
-有些情况下可以不写,此时称为浅拷贝;
-有些情况必须写,此时称为深拷贝。
类中如果含有指针成员,执行默认复制构造函数时(浅拷贝):
-会将一个原对象的指针成员复制到新对象的指针成员;
-若指针成员指向堆内存,在原对象生存期结束时,析构函数将堆内存释放,则新对象的指针成员会变为悬挂指针,新对象生存期结束时析构函数会出错。
通常来说,复制构造函数的参数是引用名。
析构函数
析构函数用于在对象生存期结束后释放掉对象所占的内存空间。
访问属性:公有成员函数
参数名:在类名前加“~”构成
无参数、无返回值、不能重载(因为没有参数)
对象也可以作为返回对象和传递参数
student f(student s){
函数体
}
传递的方法是值传递。
下面为一个例子:
class Tdate{
public:
void Tdate(int,int,int); //构造函数,实参传递给形参自动调用复制构造函数(可带默认值)
void Print();
private:
int month;
int day;
int year;
};
void someFunction(Tdate someday){
someday.print();
}
int main(){
Tdate s(2003, 15, 2);
someFunction(s);
........
}
return返回对象时自动执行复制构造函数。
类类型的指针及引用可以作为函数的参数及返回值。由于传递的是指针,不会执行复制构造函数。
Tdate &someFunc(Tdate &someday){ //这里的&表示引用运算符。
return someday;
}
int main(){
Tdate s(2023,15,2);
Tdate t = someFunc(s);
}
Tdate *someFunc(Tdate *someday){
return someday;
}
int main(){
Tdate s(2003, 15, 2);
Tdate t = *someFunc(&s);//对于指针变量,传入时要取地址;*表示取地址的值。赋值给t的时候会调用复制构造函数。
}
对象指针和堆对象
使用对象指针访问对象成员,使用->运算符。语法形式为:对象指针名->公有成员。
class Clock{
public:
Clock(int H, int M, int S);
void setTime(int H, int M, int S);
void showTime();
private:
int Hour;
int Min;
int Sec;
}
int main(){
Clock* pmyclock = new Clock;//在堆区分配一个Clock的存储空间
pmyclock->setTime(12,5,0);
pmyclock->showTime();
delete pmyclock;
}
this指针
在类的外部访问类的成员必须要通过对象来调用。
在成员函数内部,访问数据成员或者成员函数时并没有看到这些成员属于哪个对象。
在 C++ 中,this 指针是一个特殊的指针,它指向当前对象的实例。
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。
this是一个隐藏的指针,可以在类的成员函数中使用,它可以用来指向调用对象。
当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为 this 指针。
友元函数没有 this 指针,因为友元不是类的成员,只有成员函数才有 this 指针。
class Clock{
public:
Clock(int H, int M, int S);
void setTime(int H, int M, int S);
void showTime();
private:
int Hour;
int Min;
int Sec;
}
void Clock::setTime(int H, int M, int S){
this->Hour = H;
this->Min = M;
this->Sec = S;
}
//在调用参数时,我们实际上省去了一个this指针
内部类和命名空间
把一个类写在另一个类的内部,就称之为内部类。
内部类和普通类在使用上几乎无区别。
外部类不能自由访问内部类,内部类不能访问外部类。
内部类主要是为了避免类名的冲突。
class AAA{
public:
class inner{
};
};
命名空间是解决名字冲突的终极方案。只要类名在一个命名空间中唯一就好了。