目录
Const关键字
常量
常量指针
参数传递
返回值
成员函数
const作用域
运算符重载
继承
继承同名静态成员函数
构造和析构的调用顺序
多重继承
菱形继承(二义性)
虚继承的工作原理
友元
常(成员)函数
Const关键字
在C++中,const关键字用于指定变量的值在初始化后不能被修改。它主要用于提高代码的安全性和可读性。
常量
const int a = 100; //常量,初始化后不可修改
常量指针
• `const`在指针前:指针指向的数据是常量,不能被修改。
• `const`在指针后:指针本身是常量,不能指向其他地址。
#include <iostream>
using namespace std;
int value1 = 100;
int value2 = 2;int main()
{const int* ptr = &value1; //常量指针,const修饰ptr指向的值,可以不用进行初始化int* const ptr1 = &value2; //指针常量,ptr1初始化后不可指向其他地址,需要进行初始化const int* const ptr3 = &value1; //ptr3指向的地址和值都不可修改return 0;
}
参数传递
• `const`修饰函数参数,可以防止函数内部修改参数的值,这对于引用和指针参数特别有用。
void print(const int& value) {} // value不能被修改
返回值
• `const`修饰返回值,可以防止返回值被修改。通常用于返回对象时,防止返回的对象被修改。
const string& getName() const { //防止返回值name被修改return name;
}
成员函数
在成员函数的末尾加上`const`,该成员函数不会修改对象的状态(即不会修改对象的任何成员变量)
class MyClass {
public:int getValue() const { // const成员函数return value;}
private:int value;
};
const作用域
//跨多个类文件(.cpp)文件使用的时候,定义和引用的地方都要加extern
//a.cpp
extern const int num =1;//b.cpp
extern const int num;//跨头文件(.h)引用时,可通过书写头文件引用
//a.h
const int value =1;//a.cpp
#include"a.h"
int main(){cont<<value;
}
运算符重载
运算符重载实质上是函数重载(重载+,-,*等函数),也是一种C++静态多态(编译时多态),静态多态是在编译时由编译器确定的不会产生额外的开销。
#include <iostream>using namespace std;class Box
{
public:int length; // 成员属性:长int width; // 成员属性:宽int height; // 成员属性:高// 默认构造函数,初始化长宽高为1Box(){length = 1;width = 1;height = 1;}// 带参数的构造函数,用于初始化长宽高Box(int length, int width, int height){this->length = length; // 使用this指针区分成员变量和参数this->width = width;this->height = height;}// 重载加法运算符,用于两个Box对象相加Box operator + (const Box& other){Box temp; // 创建一个临时Box对象temp.height = this->height + other.height; // 高度相加temp.width = this->width + other.width; // 宽度相加temp.length = this->length + other.length; // 长度相加return temp; // 返回临时Box对象}// 重载赋值运算符,用于将一个Box对象的值赋给另一个Box对象Box& operator = (const Box& other){this->height = other.height; // 赋值高度this->width = other.width; // 赋值宽度this->length = other.length; // 赋值长度return *this; // 返回当前对象的引用,支持链式赋值}// 重载累加运算符,用于将一个Box对象的值累加到当前Box对象void operator +=(const Box& other){this->height += other.height; // 高度累加this->width += other.width; // 宽度累加this->length += other.length; // 长度累加}// 重载前++运算符,用于将当前Box对象的长宽高各加1void operator ++ (){cout << "前++" << endl;this->height++; // 高度加1this->width++; // 宽度加1this->length++; // 长度加1}// 重载后++运算符,用于将当前Box对象的长宽高各加1,并返回加1前的值Box operator ++(int) // 有占位符的是后++{cout << "后++" << endl;Box other = *this; // 先保存当前对象的值this->height++; // 高度加1this->width++; // 宽度加1this->length++; // 长度加1return other; // 返回加1前的值}// 重载小于运算符,用于比较两个Box对象的长度bool operator<(const Box& other){return this->length < other.length; // 比较长度}// 重载等于运算符,用于比较两个Box对象的长度是否相等bool operator == (const Box& other){return this->length == other.length; // 比较长度}
};// 重载输出流运算符,用于输出Box对象的信息
ostream& operator<<(ostream& out, Box other)
{out << other.height << " " << other.length << " " << other.width << endl; // 输出高、长、宽return out; // 返回输出流对象,支持链式输出
}int main()
{Box a, b; // 创建两个Box对象a和b,默认初始化Box c; // 创建一个Box对象c,默认初始化c = a + b; // 调用重载的加法运算符,计算a和b的和,并赋值给ca = b = c; // 调用重载的赋值运算符,将c的值赋给b,再将b的值赋给aa += b; // 调用重载的累加运算符,将b的值累加到a++a; // 调用重载的前++运算符,将a的长宽高各加1cout << "a.height:" << a.height << " a.width:" << a.width << " a.length:" << a.length << endl; // 输出a的长宽高a++; // 调用重载的后++运算符,将a的长宽高各加1cout << "a.height:" << a.height << " a.width:" << a.width << " a.length:" << a.length << endl; // 输出a的长宽高cout << a << b << c << endl; // 调用重载的输出流运算符,输出a、b、c的信息return 0;
}
继承
继承同名静态成员函数
class father
{
public:static void fun(){cout << "father's fun" << endl;}
};
class son :public father
{
public:static void fun(){cout << "son's fun" << endl;}
};
int main()
{son::fun();//输出"son's fun"son::father::fun();//输出"father's fun"return 0;
}
构造和析构的调用顺序
- 构造函数:派生类(子类)的构造函数会首先调用基类的构造函数来初始化基类(父类)部分,然后初始化派生类部分。如果基类有默认构造函数,可以不显式调用。
- 析构函数:析构函数的调用顺序与构造函数相反,先调用派生类的析构函数,再调用基类的析构函数
#include <iostream>using namespace std;class father
{
public:int temp;father(int a){cout << "父类构造1" << endl;}father(int a, int b){cout << "父类构造2" << endl;}void fun(){cout << "father fun" << endl;}~father(){cout << "父类析构" << endl;}
};
class son :public father
{
public:son() :father(2, 4)//父类没有无参构造,子类声明父类走的哪个构造{cout << "子类构造" << endl;}~son(){cout << "子类析构" << endl;}
};
int main()
{son s;return 0;
}
多重继承
C++支持多重继承,子类可同时继承多个父类
class B {};class C {};class D : public B, public C {};
菱形继承(二义性)
菱形继承会产生二义性问题(D中继承的变量a,不知道是来自类B还是类C),可以通过虚继承或利用作用域来解决二义性
虚继承的工作原理
- 共享基类实例:通过虚继承,类B和类C共享同一个类A的实例,而不是各自拥有一个独立的实例。
- 构造函数调用:当创建类D的对象时,类A的构造函数只会被调用一次。这是通过在类D的构造函数中显式调用类A的构造函数来实现的。如果类D的构造函数中没有显式调用类A的构造函数,则会调用类A的默认构造函数(如果存在).
- 访问路径:由于类A的实例是共享的,类D可以通过类B或类C访问类A的成员,但访问路径是唯一的,因为类A的实例只有一个.
友元
友元可以让一个类访问另一个类私有成员,使用友元函数/类 的优缺点:
优点:提高函数效率,表达清晰简单
缺点:破坏类的封装机制,尽量不使用
class A
{
private:int temp=1;friend void fun();//声明函数fun为类A的友元函数friend class B;//声明类B为类A的友元函数,在类B中可以访问类A的私有成员变量
};
void fun()
{A a;cout <<"a.temp: "<< a.temp << endl;
}
class B
{
public:void Api(){A a;cout << "a.temp: " << a.temp << endl;}
};
int main()
{fun();B b;b.Api();return 0;
}
常(成员)函数
- 定义:常函数使 调用类的成员函数不会对类做任何修改
- 格式:返回值 函数名(参数)const {函数体}
- 特点:
- 可读不可写数据成员,也就是无法修改
- 常函数的this指针式const Class *型
- 常成员函数只能调用常成员函数,不能更改类中的成员状态(与const相似)
- 常函数能修改传入自身的形参以及内部定义的局部变量
- 常对象只能调用常函数,不能调用普通函数。
#include <iostream>using namespace std;class Student
{
public:Student() {}Student(const string& nm, int sc = 0) :name(nm), score(sc) {}void set_student(const string& nm, int sc = 0){name = nm;score = sc;}const string& get_name()const;//常函数int get_score() const{return score;}
private:string name;int score;
};
const string& Student::get_name()const
{//set_student();//常函数只能调用常函数,不能调用类中未使用const修饰的成员函数。get_score();return name;
}
void output_student(const Student& student)
{cout << "student.get_score(): " << student.get_score() << endl;
}
int main()
{Student stu("wang",85);output_student(stu);
}