C++基础第六弹
C++面向对象
1.C++类 & 对象
- 类是C++的核心特性,通常被称为用户定义的类型
- 类用于指定对象的形式,是一种用户自定义的数据类型,是一种封装了数据和函数的组合
- 类中的数据称为成员变量
- 类中的函数称为成员函数
- 类可以被看作是一种模板,用来创建具有相同属性和行为的多个对象
1.1C++类定义
定义类需要用到关键字 class,然后指定类的名称,类的主体是包含在一对花括号中,主体包含类的成员变量和成员函数
语法:
class 类名{ 访问修饰符(public/protected/private): 成员变量; 成员函数(); }
1.2C++对象
- 对象是根据类来创建的,声明类的对象,就像声明基本类型的变量一样
1.3访问数据成员
类的对象的公共数据成员可以使用直接成员访问运算符
.
来访问// // Created by 16690 on 2024/4/21. // #include <iostream> using namespace std; class Box { public: //成员变量声明 double length; double breadth; double height; //成员函数声明 double get(void); void set(double len, double bre, double hei); }; //成员函数定义 //get函数 double Box::get(void) { return length * breadth * height; } //set函数 void Box::set(double len, double bre, double hei) { length = len; breadth = bre; height = hei; } int main(void) { Box box1; Box box2; Box box3; double volume = 0.0; //box1信息 box1.height = 5.0; box1.length = 6.0; box1.breadth = 7.0; //box2信息 box2.height = 10.0; box2.length = 12.0; box2.breadth = 13.0; //box1体积 volume = box1.height * box1.length * box1.breadth; cout << "box1体积:" << volume << endl; //box2体积 volume = box2.height * box2.length * box2.breadth; cout << "box2体积:" << volume << endl; box3.set(16.0, 18.0, 20.0); volume = box3.get(); cout << "box3体积:" << volume << endl; return 0; }
1.4类成员函数
类的成员函数是指哪些把定义和原型写在类定义内部的函数,就像类定义中的其它变量一样
类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员
成员函数可以定义在类定义内部,或**单独使用范围解析运算符
::
**来定义,在类定义中定义的成员函数把函数声明为内联,即便没有inline标识符使用范围解析运算符
::
,也叫作用域区分符,指明一个函数属于哪个类或一个数据属于哪一个类。::
运算符之前必须使用类名,调用成员函数是在对象上使用点运算符.
Box box; box.get(); ------------------------------------------------------------ Box::get(){ }
未使用类成员函数
class Box { public: //成员变量声明 double length; double breadth; double height; //成员函数声明 double get(void); void set(double len, double bre, double hei); }; //成员函数定义 //get函数 double Box::get(void) { return length * breadth * height; } //set函数 void Box::set(double len, double bre, double hei) { length = len; breadth = bre; height = hei; }
使用类成员函数
class Boxs{ //成员变量声明 double length; double breadth; double height; //成员函数声明 //使用inline标识符 inline double get(void){ return length * breadth * height; } //默认内联标识符 void set(double len, double bre, double hei){ length = len; breadth = bre; height = hei; } };
1.5类访问修饰符
类成员的访问限制是通过在类主体内部对各个区域标记
public、protected、private
来指定的,一个类可以有多个public、protected、private标记区域,每个标记区域在下一个标记区域开始之前或在遇到类主体结束右括号之前都是有效的
成员和类的默认访问修饰符是private
class Base{ public : //公有成员 protected: //受保护成员 private: //私有成员 };
公有public成员
- 公有成员在程序中类的外部是可访问的,可以不适用任何成员函数来设置和获取公有变量的值
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class Base{
public :
//公有成员
double length;
void setLength(double len);
double getLength(void);
};
void Base::setLength(double len){
length = len;
}
double Base::getLength(void ) {
return length;
}
int main(void){
//测试公有成员
Base base;
//设置长度
base.setLength(10.0);
cout << "测试公有成员变量:base.getLength() = " << base.getLength() << endl;
//因为是公有的,所以可以直接设置长度
base.length = 20.0;
cout << "测试公有成员变量:base.length = " << base.length << endl;
return 0;
}
私有private成员
- 私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员
- 默认情况下,类的所有成员都是私有的
- 一般情况下,在私有区域定义数据,公有区域定义相关的函数
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class Base{
//默认私有成员
double width;
public:
inline void setWidth(double wid){
width = wid;
}
inline double getWidth(void){
return width;
}
};
int main(void){
Base base;
//不使用成员函数设置长度
// base.width = 10;
base.setWidth(10);
cout << "length of base:" << base.getWidth() << endl;
return 0;
}
受保护protected成员
- protectd成员变量或函数与私有成员十分类似,但有一点不同,protected成员在派生类(子类)中是可以访问的
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class Base {
protected:
double height;
};
class SunBase:Base{
public :
void setSunHeight(double hei);
double getSunHeight();
};
void SunBase::setSunHeight(double hei) {
height = hei;
}
double SunBase::getSunHeight() {
return height;
}
int main(void) {
SunBase sun;
sun.setSunHeight(10.0);
cout << "Sun height is " << sun.getSunHeight() << endl;
return 0;
}
继承中的特点
- public继承:基类public成员,protected成员,private成员的访问属性在派生类中分别变成:public、protected、privated
- procted继承:基类public成员,protected成员,private成员的访问属性在派生类中分别变成:protected、protected、privated
- private继承:基类public成员,protected成员,private成员的访问属性在派生类中分别变成:private、private、private
- private成员只有本类成员(类内)和友元访问,不能被派生访问
- protected成员可以被派生类访问
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class A {
public:
int a;
A() {
a1 = 1;
a2 = 2;
a3 = 3;
a = 4;
}
void fun() {
cout << a << endl;
cout << a1 << endl;
cout << a2 << endl;
cout << a3 << endl;
}
public:
int a1;
protected:
int a2;
private:
int a3;
};
class B : public A {
public:
int a;
B(int i) {
A();
a = i;
}
void fun() {
cout << "打印b中的fun=" << a << endl;
cout << "打印b中的fun=" << a1 << endl;
cout << "打印b中的fun=" << a2 << endl;
// cout << a3 << endl; 报错,因为private成员不能够派生类访问
}
};
int main(void) {
B b(10);
cout << b.a << endl;
cout << b.a1 << endl;
b.fun();
// cout << b.a2 << endl; //除了父类及子类能够在本类中访问protected成员
// cout << b.a3 << endl; //private 成员不允许在外部直接访问
return 0;
}
类访问修饰符 | 基/子类的public成员 | 基/子类的protected成员 | 基/子类的private成员 |
---|---|---|---|
public | public成员可见 | protected成员可见 | 不可见 |
protected | protected成员可见 | protected成员可见 | 不可见 |
private | private成员可见 | private成员可见 | 不可见 |
1.6构造函数 & 析构函数
类的构造函数
- 类的构造函数是类的一种特殊的成员函数,会在每次创建类的新对象时执行
- 构造函数的名称与类的名称完全相同,且不会返回任何类型,也不会返回void
- 构造函数可用于为某些成员变量设置初始值
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class Line{
public:
void setLength(double len);
double getLength(void);
//构造函数
Line();
private:
double length;
};
//填写构造函数内容
Line::Line(void){
cout << "Object is being created" << endl;
}
void Line::setLength(double len) {
length = len;
}
double Line::getLength() {
return length;
}
int main(void){
Line line;
//设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
return 0;
}
带参数的构造函数
- 默认的构造函数是没有任何参数,若需要,构造函数也可以带有参数,这样在创建对象时就会给对象赋初值
//
// Created by 16690 on 2024/4/21.
//
#include <iostream>
using namespace std;
class Line{
public:
void setLength(double len);
double getLength(void);
void setWidth(double wid){
width = wid;
}
double getWidth(void){
return width;
}
//构造函数
Line(void);
Line(double wid);
private:
double length;
double width;
};
//无参构造函数内容
Line::Line(void){
cout << "Object is being created" << endl;
}
//有参构造函数内容
Line::Line(double wid){
cout << "Object is being created" << endl;
width = wid;
}
void Line::setLength(double len) {
length = len;
}
double Line::getLength() {
return length;
}
int main(void){
Line line;
//设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() << endl;
//设置宽度
Line lineWid(2.0);
cout << "Width of line : " << lineWid.getWidth() << endl;
return 0;
}
使用初始化列表来初始化字段
//
// Created by 16690 on 2024/4/21.
//
#include<iostream>
using namespace std;
class Line{
public:
void setLength(double len);
double getLength(void);
//构造函数
Line() : length(0), width(0) {
cout << "Default constructor called" << endl;
}
Line(double len) : length(len), width(0) {
cout << "Single-parameter constructor called, length = " << length << endl;
}
Line(double wid, double len) : width(wid), length(len) {
cout << "Two-parameter constructor called, length = " << length << ", width = " << width << endl;
}
private:
double length;
double width;
};
void Line::setLength(double len) {
length = len;
}
double Line::getLength() {
return length;
}
int main(void) {
Line line1;
Line line2(10.0);
Line line3(10.0, 20.0);
// 设置长度
line1.setLength(5.0);
cout << "Length of line1 : " << line1.getLength() << endl;
return 0;
}
//
// Created by 16690 on 2024/4/21.
//
#include<iostream>
using namespace std;
class Line{
public:
//构造函数
Line(void);
Line(double len);
Line(double len, double wid);
void setLength(double len);
double getLength(void);
private:
double length;
double width;
};
Line::Line(void) : length(0),width(0){
cout << "Default constructor called" << endl;
}
Line::Line(double len):length(len),width(0) {
cout << "Single-parameter constructor called, length = " << length << endl;
}
Line::Line(double len, double wid):length(len),width(wid) {
cout << "Two-parameter constructor called, length = " << length << ", width = " << width << endl;
}
void Line::setLength(double len) {
length = len;
}
double Line::getLength() {
return length;
}
int main(void) {
Line line1;
Line line2(10.0);
Line line3(10.0, 20.0);
// 设置长度
line1.setLength(5.0);
cout << "Length of line1 : " << line1.getLength() << endl;
return 0;
}
类的析构函数
类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行
析构函数的名称与类的名称是完全相同的,就只在前面加了个波浪号(~)作为前缀,不会返回任何值,也不能带有任何参数
// // Created by 16690 on 2024/4/21. // #include <iostream> using namespace std; class Line{ public: void setLength(double len); double getLength(void); //构造函数声明 Line(); //析构函数声明 ~Line(); private: double length; }; Line::Line(void) { cout << "Object is being created" << endl; } Line::~Line(void) { cout << "Object is being deleted" << endl; } void Line::setLength(double len) { length = len; } double Line::getLength(void) { return length; } int main(void){ Line line; //设置长度 line.setLength(6.0); cout << "Length of line : " << line.getLength() << endl; return 0; }
1.7拷贝构造函数
- 拷贝构造函数是一种特殊的构造函数,在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象
- 拷贝构造函数通常用于
- 通过使用另一个同类型的对象来初始化新创建的对象
- 复制对象把它作为参数传递给函数
- 复制对象,并从函数返回这个对象
若在类中没有定义拷贝构造函数,编译器会自行定义一个,若类带有指针变量,并有动态内存分配,则必须有一个拷贝构造函数
默认的拷贝构造函数实现的只能是浅拷贝,即直接将原对象的数据成员值依次复制给新对象中对应的数据成员,并没有为新对象另外分配内存资源。
当类的数据成员中有指针类型时,我们就必须定义一个特定的拷贝构造函数,该拷贝构造函数不仅可以实现原对象和新对象之间数据成员的拷贝,而且可以为新的对象分配单独的内存资源
语法:
类名 (const 类名 &obj){ //构造函数的主体 }
// // Created by 16690 on 2024/4/21. // #include <iostream> using namespace std; class Line{ public: int getLength(void); //简单构造函数 Line(int len); //拷贝构造函数 Line(const Line &obj); //析构函数 ~Line(); private: int *ptr; }; Line::Line(int len){ cout << "调用构造函数" << endl; //为指针分配内存 ptr = new int; *ptr = len; cout << "构造函数:" <<ptr << endl; } Line::Line(const Line &obj) { cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl; ptr = new int; *ptr = *obj.ptr; cout << "拷贝构造函数:" <<ptr << endl; } Line::~Line() { cout << "释放内存" << endl; delete ptr; } int Line::getLength( void ) { return *ptr; } void display(Line obj){ cout << "line length: " << obj.getLength() << endl; } int main(void){ Line line(10); display(line); return 0; }
1.8友元函数
类的友元函数是定义在类外部,但有权访问类的私有(private)成员和保护(protected)成员,尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数
友元可以是一个函数,该函数被称为友元函数
友元可以是一个类,该类被称为友元类
若要声明函数为一个类的友元,需要在类定义中在该函数原型前使用关键字friend
//友元函数 friend 返回类型 函数名(); //声明某个类的所有成员作为另一个类的友元 friend class 类名;
- 若要访问非static成员时,需要对象做参数
- 若要访问static成员或全局变量时,则不需要对象做参数
- 若要做参数的对象是全局对象,则不需要对象做参数
- 直接调用友元函数,不需要通过对象指针
// // Created by 16690 on 2024/4/22. // #include <iostream> using namespace std; class Box{ double width; public: friend void printWidth(Box box); friend class Boxs; void setWidth(double wid); }; class Boxs{ public: void Print(int width,Box &box){ box.setWidth(width); //因为Boxs是Box的友元,可以访问Box的私有成员 cout << "Width of box : " << box.width << endl; } }; void Box::setWidth(double wid) { width = wid; } void printWidth(Box box){ // printWidth是Box的友元,可以直接访问该类的任何成员 cout << "Width of box : " << box.width << endl; } int main(void){ Box box; //使用成员函数设置宽度 box.setWidth(10.0); //使用友元函数打印宽度 printWidth(box); return 0; }
1.9内联函数
通常与类一起使用,若一个函数是内联的,那么在编译时,编译器会将函数的代码副本放置在每个调用该函数的地方
对内联函数进行任何修改,都需要重新编译函数的所有客户端,因为编译器需要重新更换一次所有的代码,否则将会继续使用旧的函数
内联函数需要在函数名前面放置关键字 inline,在调用函数之前需要对函数进行定义。
如果已定义的函数多于一行,编译器会忽略 inline 限定符。
在类定义中的定义的函数都是内联函数,即使没有使用 inline 说明符
// // Created by 16690 on 2024/4/22. // #include <iostream> using namespace std; inline int Max(int x, int y) { return (x > y) ? x : y; } int main(void) { cout << "Max(10, 5) = " << Max(10, 5) << endl; cout << "Max(20, 15) = " << Max(20, 15) << endl; cout << "Max(0, -10) = " << Max(0, -10) << endl; return 0; }
1.10C++中的this指针
- 在C++中,this指针是一个特殊的指针,它指向当前对象的实例
- 每个对象都能通过this指针来访问自己的地址
- this是一个隐藏的指针,可以在类的成员函数中使用,可以用来指向调用对象
- 当一个对象的成员函数被调用时,编译器会隐式地传递该对象的地址作为this指针
- 友元函数没有this指针,因为友元不是类的成员,只有成员函数才有this指针
- 需要用
this -> 成员变量/函数
//
// Created by 16690 on 2024/4/22.
//
#include <iostream>
using namespace std;
class A {
private :
int value;
public:
void setValue(int value) {
this->value = value;
}
int getValue() {
return value;
}
void print() {
cout << "value = " << this->value << endl;
}
};
int main(void) {
A a;
a.setValue(10);
cout << "gatValue = " << a.getValue() << endl;
a.print();
return 0;
}
1.11C++指向类的指针
- 指向C++类的指针与指向结构的指针类似,访问之相类的指针的成员,需要使用成员访问运算符
->
- 指向类的指针指向一个类的对象,指向类的指针可以用于访问对象的成员变量和成员函数
声明和初始指向类的指针
//
// Created by 16690 on 2024/4/22.
//
#include <iostream>
using namespace std;
class MyClass {
public:
int data;
void display() {
cout << "data = " << data << endl;
}
};
int main(void) {
//创建类对象
MyClass obj;
obj.data = 42;
//声明和初始化指向类的指针
MyClass *ptr = &obj;
//通过指针访问成员变量
cout << "通过指针访问成员变量:" << ptr->data << endl;
//通过指针调用成员函数
ptr->display();
return 0;
}
动态分配内存
//
// Created by 16690 on 2024/4/22.
//
#include <iostream>
using namespace std;
class MyClass {
public:
int data;
void display() {
cout << "data = " << data << endl;
}
};
int main(void) {
//动态分配内存创建类对象
MyClass *ptr= new MyClass();
ptr->data = 10;
//通过指针访问成员变量
cout << "通过指针访问成员变量:" << ptr->data << endl;
//通过指针调用成员函数
ptr->display();
//释放内存
delete ptr;
return 0;
}
指向类的指针作为函数参数
//
// Created by 16690 on 2024/4/22.
//
#include <iostream>
using namespace std;
class MyClass {
public:
int data;
void display() {
cout << "data = " << data << endl;
}
};
void processObject(MyClass *ptr){
ptr -> display();
}
int main(void) {
//动态分配内存创建类对象
MyClass obj;
obj.data = 10;
processObject(&obj);
return 0;
}
1.12C++类的静态成员
使用 static 关键字来把类成员定义为静态的,当我们声明类的成员为静态时,无论创建多少个类的对象,静态成员都只有一个副本。
静态成员在类的所有对象中是共享的,如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用范围解析运算符 :: 来重新声明静态变量从而对它进行初始化
// // Created by 16690 on 2024/4/22. // #include<iostream> using namespace std; class Box{ public: static int objectCount; //构造函数定义 Box(double len=2.0,double wid=2.0,double hei=2.0){ cout << "构造函数执行" << endl; length = len; width = wid; height = hei; objectCount++; } double volume(){ return length * width * height; } private: double length; double width; double height; }; //初始化静态成员变量 int Box::objectCount = 0; int main(void){ Box box1(3.3, 1.2, 1.5); Box Box2(8.5, 6.0, 2.0); cout << "Total objects:" << box1.objectCount << endl; return 0; }
静态成员函数
- 如果把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来
- 静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问
- 静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数
- 静态成员函数有一个类范围,他们不能访问类的 this 指针
静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针
// // Created by 16690 on 2024/4/22. // #include<iostream> using namespace std; class Box{ public: static int objectCount; //构造函数定义 Box(double len=2.0,double wid=2.0,double hei=2.0){ cout << "构造函数执行" << endl; length = len; width = wid; height = hei; objectCount++; } double volume(){ return length * width * height; } static int getCount(){ return objectCount; } private: double length; double width; double height; }; //初始化静态成员变量 int Box::objectCount = 0; int main(void){ cout << "创建对象前,共有" << Box::getCount<< "个对象" << endl; Box box1(3.3, 1.2, 1.5); Box Box2(8.5, 6.0, 2.0); cout << "Total objects:" << box1.objectCount << endl; return 0; }