Loading

Loading

深圳大学面向对象程序设计(OOP)期末复习

深圳大学面向对象程序设计(OOP)期末复习

所用课本是钱能的《C++程序设计教程》,2025-2026学年春季学期人工智能学院的OOP课程

第十一章 类

类的作用是封装,可以封装多种函数,使得他们不会轻易被外部修改。

一般采用这样的格式:

class Student {
private:
    // 数据成员

public:
    // 构造函数

    // getter / setter

    // 其他成员函数
};

:: 称为作用域解析运算符。

Rectangle::area 表示 area 是 Rectangle 类作用域中的成员

使用类名::函数名,可以明确函数属于哪个类

完整的类外实现

#include <iostream>
using namespace std;

class Rectangle {
private:
    double width;
    double height;

public:
    // 构造函数声明。
    Rectangle(double newWidth, double newHeight);

    // 普通成员函数声明。
    double area() const;
	//函数后面的const表面这个成员函数不会修改当前对象
    double perimeter() const;
};

// 构造函数类外实现。
Rectangle::Rectangle(double newWidth, double newHeight)
    : width(newWidth), height(newHeight) {
}

// area 类外实现。
double Rectangle::area() const {
    return width * height;
}

// perimeter 类外实现。
double Rectangle::perimeter() const {
    return 2 * (width + height);
}

int main() {
    Rectangle rect(3.0, 4.0);

    cout << rect.area() << '\n';
    cout << rect.perimeter() << '\n';

    return 0;
}

初始化列表:成员直接被初始化为目标值,比函数体赋值更好,函数体赋值时先完成默认初始化,再被重新赋值。

Student(const string& newName,int newScore)
	:name(newName),score(newScore){
    }

构造函数重载

同一个类可以有多个构造函数,只要参数列表不同。

class Point {
private:
    int x;
    int y;

public:
    // 无参构造函数。
    Point()
        : x(0), y(0) {
    }

    // 一个参数构造函数。
    Point(int value)
        : x(value), y(value) {
    }

    // 两个参数构造函数。
    Point(int newX, int newY)
        : x(newX), y(newY) {
    }
};

使用这样的方式

Point a;
Point b(3);
Point c(3, 4);

就可以分别调用不同构造函数。

这就是构造函数重载

析构函数

在对象生命周期结束时自动调用的特殊成员函数

什么时候运行的位置离开作用域,什么时候执行析构函数。

析构函数不能写返回类型,不能带参数。

动态数组的申请与释放

class IntArray{
private:
	int* data;
    int size;
public:
	IntArray(int n)
		:size(n){
		//动态申请数组
		data=new int[size]
    }
    ~IntArray(){
        //释放动态数组,由于是多个对象所以是delete[]
        delete[] data;
    }
};

静态成员与友元

继承

在已有类的基础上定义一个新类,新类自动拥有原类中的部分成员

比如先定义公共的A类

//基类
class A{
};

再定义出派生类B和C:

//派生类
class B:public A{
};

class C:public A{
};

最简单的继承

#include<bits/stdc++.h>
using namespace std;
//定义基类Person
class Person{
    public:
    string name;
    int age;
    
    void displayBasicInfo() const{
        cout<<name<<" "<<age;
    }
};
//Student公有继承Person
class Student:public Person{
  public:
    int score;
    
    void display() const{
        //name和age继承person,score是student中自己定义的成员
        cout<<name<<" "<<age<<" "<<score<<endl;
    }
};

int main(){
    Student stu;
    //Student 对象可以使用继承来的成员。
    stu.name="Alice";
    stu.age=19;
    //score 是 Student 自己新增的成员。
    stu.score=95;
    stu.display;
	return 0;
}

使用继承就不用重复定义很多变量了。

class的默认继承方式

class Student:Person{};

这种方式默认为private继承,即:

class Student:private Person{};

所以不要忘记写public!!!!!

区分三种成员访问权限

  • private: 本类内部可以访问 派生类不能直接访问 类外不能访问

  • protected: 本类内部可以访问 派生类可以访问 类外不能访问

  • public: 本类内部可以访问 派生类可以访问 类外可以访问

多态

指向同一个接口,根据不同对象表现不同。

三个条件:

  1. 有继承

    class Dog::public Animal
    
  2. 父类条件必须virtual

    class Animal{
    	virtual void speak();
    }
    
  3. 子类重写

    void speak() override
    
  4. 用父类指针或引用调用

    Animal* p=&dog;
    p->speak();
    

多继承

一个孩子两个爸爸。

class SmartPhone
	:public Camera,
	public Phone
{

};

虚继承

用于解决多继承产生重复父类的问题,即菱形继承问题。

看下图中的TA,就会出现两个person,一个来自student,一个来自teacher

        Person

       /     \

 Student   Teacher

       \     /

        TA

解决方法:

class Student
:
virtual public Person
{

};


class Teacher
:
virtual public Person
{

};

这样TA只有一个Person

再总结一下代码模板框架:

  1. 普通继承

    class child
    :
    public Parent{};
    
  2. 派生构造

    Child()
    :
    Parent(){}
    
  3. 虚函数

    父类(virtual)

    class Animal{
    	public:
    	virtual void speak(){}
    };
    

    子类(override)

    class Dog::public Animal
    {
        public:
        void speak() override
        {
        }
    };
    

    调用

    Animal* p=new Dog;
    p->speak();
    
  4. 多继承

    class C
    :
    public A,
    public B
    {
    };
    

运算符重载

方法1:成员函数(private变量不能访问)

返回类型 operator符号(参数)
{

}

例如

Complex operator+(Complex a,Complex b)
{
    return Complex(a.real+b.real,a.imag+b.imag);
}
//调用a+b相当于a.operator+(b),左边对象隐藏传入

方法2:友元函数(private变量可以访问)

//friend 返回类型 operator符号(参数1,参数2)
class Complex
{
private:
int real;
    
friend Complex operator+(Complex a,Complex b);
};

IO流重载

输入

friend istream& operator>>(istream& in,ClassName& obj)
{
    in>>obj.xxx;
    return in;
}

输出

friend ostream& operator<<(ostream& out,ClassName& obj)
{
    out<<obj.xxx;
    return out;
}

来一个具体的例子

#include<bits/stdc++.h>
using namespace std;
class Complex{
	private:
	int real,imag;
	public:
		//构造函数 
		Complex(int real=0,int imag=0)
		:real(real),imag(imag){
		}
		//重载加法 
		friend Complex operator+(Complex& a,Complex& b)
		{
			return Complex(a.real+b.real,a.imag+b.imag);	
		} 
		//重载输出 
		friend ostream& operator<<(ostream& out,Complex& a)
		{
			out<<a.real<<"+"<<a.imag<<"i";
			return out;
		}
};
int main()
{
	Complex a(1,2);
	Complex b(3,4);
	Complex c=a+b;
	cout<<c; 
	return 0;
}

拷贝构造函数

复制类

基础形式:

Student(Student& s){}

特点是函数名和类名一样,没有返回值,参数必须是同类型对象引用。

需要自己写拷贝构造的情况有两种:

  1. 普通变量没问题
  2. 指针变量有问题

如果普通构造一个类,那private中的两个指针会指向同一个地址,这就是浅拷贝

a.score ----\
             \
              --->100分
             /
b.score ----/

浅拷贝是危险的,如果析构函数中 delete 这个内存,那么浅拷贝后会被delete两次,程序会崩溃。

所以我们需要深拷贝来重新申请空间

class Student{
private:
int* score;
public:
Student(int s)
{
	score=new int(s);
}
Student(const Student& s)
{
	score=new int(*s.score);//深拷贝
}
~Student(){
	delete score;
}
};

友元

允许非成员函数访问private元素

class Metal
{

private:

    int hardness;

    int weight;

    int volume;


public:

    Metal(int h,int w,int v);


    friend Metal operator+
    (
        const Metal& a,
        const Metal& b
    );


};

模板

大概长这样,嗯。

#include<bits/stdc++.h>
using namespace std;
template<class T>
T max(T arr[],int len)
{
	T mx=arr[0];
	for(int i=1;i<len;i++)
	{
		if(arr[i]>mx) mx=arr[i];
	 } 
	 return mx;
}
template<class T>
class Cryption{
	private:
		T ptxt[100];
		T ctxt[100];
		T key;
		int len;
	public:
		Cryption(T tk,T tt[],int tl);
		void Encrypt();
		void Print();
};
template<class T>
Cryption<T>::Cryption(T tk,T tt[],int tl)
{
	key=tk;
	len=tl;
	for(int i=0;i<len;i++) ptxt[i]=tt[i];
}
template<class T>
void Cryption<T>::Encrypt()
{
	T mx=max(ptxt,len);
	for(int i=0;i<len;i++)
	{
		ctxt[i]=key+(mx-ptxt[i]);
	}
}
template<class T>
void Cryption<T>::Print()
{
	for(int i=0;i<len;i++)
	{
		if(i!=len-1)
			cout<<ctxt[i]<<" ";
		else
			cout<<ctxt[i];
	}
	cout<<endl;
}
int main()
{
	int i,length,ik,itxt[100];
	double dk,dtxt[100];
	char ck,ctxt[100];
	
	cin>>ik>>length;
	for(int i=0;i<length;i++) cin>>itxt[i];
	Cryption<int> ic(ik,itxt,length);
	ic.Encrypt();
	ic.Print();
	
	cin>>dk>>length;
	for(int i=0;i<length;i++) cin>>dtxt[i];
	Cryption<double> dc(dk,dtxt,length);
	dc.Encrypt();
	dc.Print();
	
	cin>>ck>>length;
	for(int i=0;i<length;i++) cin>>ctxt[i];
	Cryption<char> cc(ck,ctxt,length);
	cc.Encrypt();
	cc.Print();
	return 0;
}

拆解一下,一般函数模板就是能兼容多种数据类型的类。

  1. 类前

    template<class T>
    class A{
        private:
        	T x,y,z;
        	int k;
    };
    
  2. 类外实现成员函数

    template<class T>
    void A<T>::fun()
    
posted @ 2026-07-12 23:11  Miya555  阅读(6)  评论(0)    收藏  举报