一,友元
友元的关键字 friend。友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
用生活例子来讲,在你的家中客厅属于公共(public),卧室属于私有(private)。所有客人都可以访问客厅,而卧室只有你自己或者你的闺蜜兄弟访问。所以在程序里,有些私有属性也想让类外的一些特殊的函数或者类进行访问,就需要用到友元技术。
友元函数示例如下:
#include <iostream>
using namespace std;
#include <string>
class Building{
//全局函数goodfriend是building的友元,可以访问building中的私有成员
friend void goodfriend(Building *building);
public:
Building(){
this->m_sittingroom="客厅"; //this指针指向被调用成员函数所属的对象
this->m_bedroom="卧室";
}
public:
string m_sittingroom;
private:
string m_bedroom;
};
//全局函数
void goodfriend(Building *building){
cout<<"好朋友可以访问"<<building->m_sittingroom<<endl;
cout<<"好朋友可以访问"<<building->m_bedroom<<endl;
}
void test1(){
Building building;
goodfriend (&building);
}
int main(){
test1();
return 0;
}
友元类,示例如下:
class Building;
class googfriend{
public:
goodfriend();
void visit();
};
class Building{
friend class goodfriend;
public:
Building();
public:
string m_sittingroom;
private:
string m_bedroom;
};
Building::Building(){
this->m_sittingroom="客厅";
this->m_bedroom="卧室";
}
goodfriend::goodfriend(){
building=new Building;
}
void goodfriend::visit(){
cout<<"好朋友访问"<<building->m_sittingroom<<endl;
cout<<"好朋友访问"<<building->m_bedroom<<endl;
}
二,重载
1.函数重载
在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。注意,只有返回类型的不同时不能用重载函数。
下面的实例中,同名函数 print() 被用于输出不同的数据类型:
#include <iostream>
using namespace std;
#include <string>
class printData
{
public:
void print(int i) {
cout << "整数为: " << i << endl;
}
void print(double f) {
cout << "浮点数为: " << f << endl;
}
void print(string c) {
cout << "字符串为: " << c << endl;
}
};
int main(void)
{
printData pd;
// 输出整数
pd.print(5);
// 输出浮点数
pd.print(500.263);
// 输出字符串
char c[] = "Hello C++";
pd.print(c);
return 0;
}
2.运算符重载
重定义或重载大部分 C++ 内置的运算符,也就是对已有的运算符重新定义。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
下列是可重载的运算符:
下面是不可重载的运算符列表:
- . 成员访问运算符
- .* ->* 成员指针访问运算符
- :: 域运算符
- sizeof 长度运算符
- ?: 条件运算符
- # 预处理符号
以加号运算符重载为例:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
三,封装,继承
(关于c++封装与继承的学习看一日攻克c++类与对象-CSDN博客)
四,多态
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。多态使得代码更加灵活和通用,程序可以通过基类指针或引用来操作不同类型的对象,而不需要显式区分对象类型。这样可以使代码更具扩展性,在增加新的形状类时不需要修改主程序。
多态的分类:
静态多态和动态多态的区别,静态多态在编译时确定函数地址,而动态多态在运行时确定函数地址。
虚函数允许子类重写它,从而在运行时通过基类指针或引用调用子类的重写版本,实现动态绑定。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。派生类可以选择性地重写虚函数,但不是必须。
在基类中声明一个函数为虚函数,使用关键字 virtual
。
动态多态满足的条件:
1.有继承关系
2.派生类重写基类的虚函数,其中函数返回值类型,函数名,参数列表必须完全相同。
示例如下:
#include <iostream>
using namespace std;
class Animal {
public:
virtual void sound() { // 虚函数
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal { //继承关系
public:
void sound() override { // 重写虚函数,返回值类型函数名相同
cout << "Dog barks" << endl;
}
};
int main() {
Animal *animal = new Dog();
animal->sound(); // 输出: Dog barks
delete animal;
}
在多态中,通常基类的虚函数的实现毫无意义,主要是调用子类重写的部分。因此将虚函数改为纯虚函数。换句话说,纯虚函数是没有实现的虚函数。当一个类中包含纯虚函数,那么这个类被称为抽象类。它不能被直接实例化,必须通过派生类实现所有纯虚函数才能创建对象。
纯虚函数的语法
注意:纯虚函数必须在基类中声明为 = 0
,表示没有实现,子类必须重写。
#include <iostream>
using namespace std;
class Shape {
public:
virtual int area() = 0; // 纯虚函数,强制子类实现此方法
};
class Rectangle : public Shape {
private:
int width, height;
public:
Rectangle(int w, int h) : width(w), height(h) { }
int area() override { // 实现纯虚函数
return width * height;
}
};
int main() {
Shape *shape = new Rectangle(10, 5);
cout << "Rectangle Area: " << shape->area() << endl; // 输出: Rectangle Area: 50
delete shape;
}
虚函数与纯虚函数对比
多态使用时,如果有子类开辟到堆区,那么父类指针在释放时无法调用子类的析构代码。此时,需要将父类中的析构函数改为虚析构或纯虚析构。
虚析构语法:
纯虚析构语法:
根据以下代码,巩固一下多态的内容:
#include <iostream>
using namespace std;
// 基类 Animal
class Animal {
public:
// 虚函数 sound,为不同的动物发声提供接口
virtual void sound() const {
cout << "Animal makes a sound" << endl;
}
// 虚析构函数确保子类对象被正确析构
virtual ~Animal() {
cout << "Animal destroyed" << endl;
}
};
// 派生类 Dog,继承自 Animal
class Dog : public Animal {
public:
// 重写 sound 方法
void sound() const override {
cout << "Dog barks" << endl;
}
~Dog() {
cout << "Dog destroyed" << endl;
}
};
// 派生类 Cat,继承自 Animal
class Cat : public Animal {
public:
// 重写 sound 方法
void sound() const override {
cout << "Cat meows" << endl;
}
~Cat() {
cout << "Cat destroyed" << endl;
}
};
// 测试多态
int main() {
Animal* animalPtr; // 基类指针
// 创建 Dog 对象,并指向 Animal 指针
animalPtr = new Dog();
animalPtr->sound(); // 调用 Dog 的 sound 方法
delete animalPtr; // 释放内存,调用 Dog 和 Animal 的析构函数
// 创建 Cat 对象,并指向 Animal 指针
animalPtr = new Cat();
animalPtr->sound(); // 调用 Cat 的 sound 方法
delete animalPtr; // 释放内存,调用 Cat 和 Animal 的析构函数
return 0;
}