C++ 面向对象 - 默认值与常量成员

发布于:2025-07-14 ⋅ 阅读:(15) ⋅ 点赞:(0)

一、 默认值

在C++中,默认值(Default Value)是一个重要的特性,它可以应用于多个场景,包括函数参数、类成员变量、模板参数、枚举初始化等。掌握默认值的各种用法能让代码更简洁、灵活且可维护。

1. 函数参数的默认值

最常用的默认值场景,允许调用函数时省略某些参数。

1.1 规则与用法
  • 声明时指定默认值(通常在头文件中):
    void greet(const std::string& name = "User");
    
  • 默认参数必须从右向左连续定义
    void foo(int a, int b = 10, int c = 20);  // 正确 ✅
    void bar(int a = 1, int b, int c = 2);    // 错误 ❌(必须从右向左连续)
    

示例

#include <iostream>

// 函数声明:第二个参数有默认值"World"
void greet(const std::string& name = "World") {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    greet();        // 输出:Hello, World!(使用默认值)
    greet("Alice"); // 输出:Hello, Alice!(显式提供值)
    return 0;
}
1.2 函数重载

默认参数可以简化某些重载情况

示例

void print(int x) { std::cout << x; }
void print(int x, int y = 0) { std::cout << x << " " << y; }

// 调用:
print(5);      // 优先匹配 print(int x, int y = 0)
print(5, 10);  // 匹配第二个

注意:如果默认参数和重载有二义性,编译器会报错:

void foo(int x = 0);
void foo();
foo();  // 错误!不知道调用哪个
1.3 注意事项
  • 默认值在声明中指定:通常在函数声明(头文件)中指定默认值,定义时不需要重复(否则可能冲突)。
  • 从右向左设置默认值:只能从右到左连续设置默认参数,不能跳过中间的参数。
  • 避免与函数重载冲突:使用默认值可以函数重载。
    greet("Alice", "Hi");  // 错误,如果想用第二个参数的默认值,不能直接跳过第一个
    

2. 类成员变量的默认值

  • 可以在类定义中为成员变量提供默认值(C++11起支持)。
  • 构造函数会优先使用初始化列表的值(如果提供了的话),否则用默认值。

三种初始化方式

方式 写法 特点
默认成员初始化(C++11) int x = 5; 适用于所有构造(无参数时)
大括号初始化(C++11) int x{}; 零初始化(0/nullptr
构造时初始化 int x(5); 显式指定初始值

示例

class Person {
private:
    std::string name = "Anonymous"; // 默认初始化
    int age{};                      // 默认0(大括号初始化)

public:
    Person() {}  // 若未显式初始化,name="Anonymous", age=0
    Person(std::string n, int a) : name(n), age(a) {}  // 覆盖默认值
};

3. 模板参数的默认值

模板类或模板函数也可以为类型参数指定默认值(类似函数默认参数)。
示例

template <typename T = int, int Size = 10>  // 默认类型=int,默认大小=10
class Array {
private:
    T data[Size];
};

int main() {
    Array<> arr1;          // 使用默认值:Array<int, 10>
    Array<double, 5> arr2; // 显式指定:Array<double, 5>
    return 0;
}

4. 函数对象(仿函数)的默认值

可以为函数对象的重载operator() 指定默认参数。

示例

struct Adder {
    int operator()(int a, int b = 10) {
        return a + b;
    }
};

int main() {
    Adder add;
    std::cout << add(5);     // 输出15 (5 + 默认10)
    std::cout << add(5, 3);  // 输出8 (5 + 3)
    return 0;
}

5. 总结

用途 适用场景 语法例子
函数参数 简化调用,提供可选参数 void foo(int a = 0);
类成员变量 C++11+默认初始化 int x = 5; / int y{};
模板参数 指定默认类型/非类型参数 template <typename T = int>
函数对象 自定义默认参数 int operator()(int a = 0);
默认初始化 避免未定义行为 int x{};x=0

🚫 注意事项:

  • 默认参数不能跳过中间的参数(必须从右向左)。
  • 默认值和函数重载必须避免二义性
  • 全局变量默认初始化为零,局部变量未定义

二、 常量成员

在C++中,常量成员(const member)是一种特殊的类成员,它可以分为两类:常量成员变量常量成员函数。理解和使用好常量成员对于编写健壮、安全的C++代码非常重要。

常量成员

  • 常量成员变量:由const修饰的成员变量,其值不可修改。

    const 类型 成员变量名
    
  • 常量成员函数:由const修饰的成员函数,其内不可修改对象成员变量的值。

     返回类型 函数名() const {}
    

1. 常量成员变量

1.1 基本概念

常量成员变量是指在类内部声明的、值不能被修改的成员变量。

1.2 声明方式
class MyClass {
public:
    const int constVar;  // 常量成员变量
};
1.3 初始化方法

常量成员变量有两种初始化方法:

  1. 默认值:可以使用默认值对类的const成员进行初始化。
  2. 构造函数初始化列表:必须在构造函数初始化列表中进行初始化,不能在构造函数体内赋值。

const 成员变量若没有默认值,就必须通过构造函数初始化。一旦初始化后,便不能再修改。
示例

class MyClass {
public:
    const int constVar1;
    const int constVar2 = 3;	// 默认值初始化
  
    MyClass(int value) : constVar1(value) {  // 正确:在初始化列表中初始化
        // constVar = value;  // 错误:不能在构造函数体内赋值
    }
	// 拥有默认值的常量成员,亦可在初始化列表中初始化
	MyClass(int value1, int value2) : constVar1(value1), constVar2(value2) {}
};
1.4 特点
  • 一旦初始化后,值不可改变
  • 必须在构造函数初始化列表中初始化
  • 通过构造函数初始化,不同对象的常量成员变量可以拥有不同的值。
1.5 使用场景

用在每个对象都拥有各自不同的不可修改的属性值,这个属性值在对象创建的时候就被确定。

  • 用于表示对象生命周期内不会改变的属性
  • 提高代码安全性,防止误修改

2. 常量成员函数

常量成员函数是一种不会修改对象状态的函数。在常量成员函数体内不允许修改成员变量。但C++也提供了例外,允许在常量成员函数中修改被 mutable 修饰的成员变量

2.1 基本概念

常量成员函数是指在函数声明和定义后加const关键字的成员函数。

2.2 声明方式
class MyClass {
public:
    void normalFunc();        // 普通成员函数
    void constFunc() const;   // 常量成员函数
};
2.3 定义

常量成员函数用于保证该函数不会修改对象的状态。当你有一个const对象或对象的引用/指针是const时,只能调用const成员函数。
若类未提供足够的 const 函数,const对象的可用操作会受限。

示例

class MyClass {
public:
    int value;
  
    void setValue(int v) { value = v; }
    int getValue() const { return value; }  // const成员函数
};

void func() {
    const MyClass obj;
    // obj.setValue(5);  // 错误:const对象不能调用非const成员函数
    int v = obj.getValue();  // 正确:可以调用const成员函数
}
2.4 特点
  • 常量成员函数不能修改类的任何普通成员变量
  • 常量成员函数可以调用其他常量成员函数
  • 常量成员函数不能调用非const成员函数
  • const对象只能调用const成员函数,不能调用非 const 函数。
2.5 重载规则

C++允许基于const的重载,即可以同时有const和非const版本的同一个函数。

class MyClass {
public:
    const int& getRef() const { return value; }
    int& getRef() { return value; }  // 重载版本的普通成员函数
  
    int value;
};
2.6 修改mutable修饰成员变量

如果确实需要在const成员函数中修改某些成员变量,可以将其声明为mutable。

class MyClass {
public:
    mutable int counter;  // 即使在const成员函数中也可以修改
  
    void increment() const {
        counter++;  // 允许修改mutable成员
    }
};

3. 总结

  1. 常量成员变量可以指定默认值,否则必须在构造函数的初始化列表中初始化,且初始化后不可修改。
  2. 常量成员函数保证不修改类成员,只能被const对象调用。
  3. 合理使用常量成员可以提高代码的安全性和可读性。
  4. mutable关键字提供了在const成员函数中修改特定成员变量的方法。

4. 综合示例

#include <iostream>
using namespace std;

class Demo{
private:
	// 常量成员变量
	const int a = 9; 	// 常量成员变量默认值
	int b;
	mutable int c = 4;    // 成员变量默认值
public:
	Demo(){}		// 常量成员变量有默认值,构造函数可以不对其进行初始化
	Demo(int a, int b):a(a),b(b){}	// 常量成员变量只能在初始化列表中初始化
//	Demo(int a){this->a = a;}		// 错误
	void setB(int b){
//		a = 4;			// 错误,成员函数中不可修改常量成员的值
		this->b = b;
	}
	
	// 常量成员函数
	void print()const{
		cout << "a = " << a << ", ";
		cout << "b = " << b << ", ";
		cout << "c = " << c << endl;
//		this->b = 9;   // 错误 常量成员函数中不可修改对象的值。
		this->c = 10;	// 常量成员函数中可修改对象的mutable成员。
	}
};


int main()
{
	Demo d;
	Demo d1(4, 7);
	
	// 不同对象拥有不同的成员常量
	d.setB(3);
	d.print();
	d1.print();
	
	// 常量对象只能调用const成员函数
	const Demo d2(5, 12);
//	d2.setB();		// 错误,常量对象不能调用非const成员函数
	d2.print();
}

网站公告

今日签到

点亮在社区的每一天
去签到