目录
const成员函数
在 C++ 中,const
成员函数 是一种特殊的成员函数,它承诺 不会修改 调用它的对象的状态(即不会修改类的成员变量)。const
成员函数的声明方式是在函数参数列表后加上 const
关键字。
1. 基本语法
class MyClass {
public:
void nonConstFunc(); // 普通成员函数(可以修改成员变量)
void constFunc() const; // const 成员函数(不能修改成员变量)
};
const
成员函数 不能修改类的非mutable
成员变量。非
const
成员函数 可以修改成员变量。
const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改,const休息Date类的Print成员函数,Print隐含的this指针由 Date * const this 变为 const Date * const this
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// void Print(const Date* const this) const
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
2. const
成员函数的作用
(1) 保证对象不被修改
如果一个对象被声明为
const
(如const MyClass obj;
),则只能调用它的const
成员函数。如果尝试调用非
const
成员函数,编译器会报错。
(2) 提高代码安全性
防止函数意外修改对象状态。
使代码更清晰,明确哪些函数会修改对象,哪些不会。
(3) 支持 const
对象
如果一个对象是
const
的,只有const
成员函数可以被调用。
3. 示例
(1) 基本用法
#include <iostream>
using namespace std;
class Counter {
private:
int count;
public:
Counter() : count(0) {}
// 普通成员函数(可以修改成员变量)
void increment() {
count++;
}
// const 成员函数(不能修改成员变量)
int getCount() const {
// count++; // 错误!不能在 const 函数里修改成员变量
return count;
}
};
int main() {
Counter c1;
c1.increment(); // 可以调用非 const 函数
cout << c1.getCount() << endl; // 可以调用 const 函数
const Counter c2;
// c2.increment(); // 错误!不能对 const 对象调用非 const 函数
cout << c2.getCount() << endl; // 可以调用 const 函数
return 0;
}
(2) const
对象只能调用 const
成员函数
const Counter c;
c.increment(); // 错误!不能调用非 const 函数
c.getCount(); // 可以调用 const 函数
(3) mutable
成员变量
如果某个成员变量被声明为 mutable
,即使在 const
成员函数中也可以修改它。
class Logger {
private:
mutable int logCount; // 可变的,即使在 const 函数里也能修改
public:
void log() const {
logCount++; // 允许修改,因为 logCount 是 mutable
}
};
4. const
成员函数的重载
可以同时提供 const
和非 const
版本的成员函数,编译器会根据调用对象的 const
性质选择合适的版本。
class Data {
private:
int value;
public:
int& getValue() { // 非 const 版本(返回可修改的引用)
return value;
}
const int& getValue() const { // const 版本(返回只读的引用)
return value;
}
};
int main() {
Data d1;
const Data d2;
d1.getValue() = 10; // 调用非 const 版本,可以修改
// d2.getValue() = 20; // 错误!调用 const 版本,不能修改
cout << d2.getValue(); // 可以读取
return 0;
}
初始化列表
初始化列表(Initializer List)是 C++ 中用于初始化类成员的一种特殊语法,它在构造函数体执行之前完成成员的初始化。
基本语法
class MyClass {
public:
// 初始化列表语法
MyClass(int a, double b)
: memberA(a),
memberB(b)
{
// 构造函数体
}
private:
int memberA;
double memberB;
};
为什么使用初始化列表
效率更高:对于类类型成员,使用初始化列表直接调用拷贝构造函数,而在构造函数体内赋值则是先调用默认构造函数再调用赋值运算符。
必须使用的情况:
初始化 const 成员
初始化引用成员
初始化没有默认构造函数的类成员
使用示例
class Example {
public:
// 必须使用初始化列表的情况
Example(int &ref, int val) : refMember(ref), constMember(val) {
// 构造函数体
}
// 初始化顺序示例
Example(int a, int b) : memberB(a), memberA(b) {
// 注意:初始化顺序取决于成员声明顺序,而非初始化列表中的顺序
}
private:
int memberA;
int memberB;
int &refMember; // 引用成员
const int constMember; // const成员
};
注意事项
初始化顺序:成员的初始化顺序与它们在类中声明的顺序一致,与初始化列表中的顺序无关。
性能考虑:对于内置类型(int, double等),使用初始化列表或在构造函数体内赋值性能差异不大,但对于类类型,初始化列表通常更高效。
默认参数:初始化列表可以与默认参数结合使用。
友元函数
一、友元函数的基本概念
友元分为友元类和友元函数
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
1.1 什么是友元函数?
友元函数(friend function)是一个被类声明为"朋友"的非成员函数,它可以访问该类的所有私有和保护成员,就像成员函数一样。友元函数不属于类的成员,但它被授予了访问类私有部分的特权。
class Box {
private:
double width;
public:
Box(double w) : width(w) {}
// 声明友元函数
friend void printWidth(const Box& box);
};
// 友元函数定义
void printWidth(const Box& box) {
// 可以访问Box的私有成员width
std::cout << "Width: " << box.width << std::endl;
}
1.2 友元函数的特点
非成员性:友元函数不是类的成员函数
特权访问:可以访问类的私有和保护成员
单向性:友元关系是单向的,类A声明函数f为友元,不意味着f是类A的成员
非传递性:友元关系不传递,A是B的友元,B是C的友元,不意味着A是C的友元
非继承性:友元关系不继承,基类的友元不是派生类的友元
二、友元函数的声明与定义
2.1 声明友元函数
友元函数在类内部声明,使用friend
关键字:
class MyClass {
private:
int secret;
public:
MyClass(int s) : secret(s) {}
// 声明普通函数为友元
friend void showSecret(const MyClass& obj);
// 声明其他类的成员函数为友元
friend void OtherClass::accessMyClass(const MyClass& obj);
};
2.2 定义友元函数
友元函数的定义与普通函数相同,不需要使用friend
关键字,但是它定义在类外面。
void showSecret(const MyClass& obj) {
std::cout << "Secret is: " << obj.secret << std::endl;
}
三、友元函数的常见用途
运算符重载
友元函数常用于重载运算符,特别是当运算符的第一个操作数不是类对象时:
class Complex {
private:
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载<<运算符为友元函数
friend std::ostream& operator<<(std::ostream& os, const Complex& c);
// 重载+运算符为友元函数
friend Complex operator+(const Complex& c1, const Complex& c2);
};
std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}
Complex operator+(const Complex& c1, const Complex& c2) {
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
static成员
在C++中,static
关键字用于类的成员时,有特殊的含义和作用。static成员分为static成员变量和static成员函数。
static成员变量
static成员变量是属于类本身的,而不是类的某个特定对象。这意味着:
类所有对象共享:所有类的对象共享同一个static成员变量
独立于对象存在:即使没有创建类的任何对象,static成员变量也存在
存储位置:存储在全局数据区,而不是对象的存储空间中
静态成员也是类的成员,受public、protected、private访问限定符的限制
声明和定义
class MyClass {
public:
static int count; // 声明static成员变量
};
int MyClass::count = 0; // 定义并初始化static成员变量(必须在类外)
使用示例
#include <iostream>
using namespace std;
class Counter {
public:
static int count; // 声明
Counter() {
count++;
}
~Counter() {
count--;
}
};
int Counter::count = 0; // 定义并初始化
int main() {
cout << "Initial count: " << Counter::count << endl;
Counter c1;
cout << "After c1: " << Counter::count << endl;
{
Counter c2;
cout << "After c2: " << Counter::count << endl;
}
cout << "After c2 destroyed: " << Counter::count << endl;
return 0;
}
static成员函数
static成员函数也是属于类而非特定对象的,具有以下特点:
只能访问static成员:不能直接访问类的非static成员变量或成员函数
没有this指针:因为没有绑定到特定对象
可通过类名直接调用:不需要通过对象实例调用
声明和定义
class MyClass {
public:
static void staticFunc(); // 声明
};
void MyClass::staticFunc() { // 定义
// 实现代码
}
使用示例
#include <iostream>
using namespace std;
class MathUtility {
public:
static int add(int a, int b) {
return a + b;
}
static int subtract(int a, int b) {
return a - b;
}
};
int main() {
cout << "5 + 3 = " << MathUtility::add(5, 3) << endl;
cout << "5 - 3 = " << MathUtility::subtract(5, 3) << endl;
return 0;
}
注意事项
static成员变量必须在类外定义和初始化(C++17引入了inline static可以在类内初始化)
static成员函数不能是const或volatile的
static成员函数不能被声明为virtual
static成员可以被继承,但遵循常规的访问控制规则
静态成员变量不能在生命位置给缺省值初始化,因为缺省值是给构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。
类型转换
C++支持内置类型隐式类型转换为类类型对象,需要有相关内置内省为参数的构造函数
构造函数前面加explicit就不再支持隐式类型转换
类类型的对象之间也可以隐式转换,需要相应的构造函数支持