类的基本思想是数据抽象(data abstraction)和封装(encapsulation)。
数据抽象是一种依赖于接口(interface)和实现(implementation)分离的编程(以及设计)技术。类的接口包括用户所能执行的操作;类的实现则包括类的数据成员、负责接口实现的函数体以及定义类所需的各种私有函数。
封装实现了类的接口和实现的分离。封装后的类隐藏了它的实现细节,也就是说,类的用户只能使用接口而无法访问实现部分。
类要想实现数据抽象和封装,需要首先定义一个抽象数据类型(abstract data type)。在抽象数据类型中,由类的设计者负责考虑类的实现过程;使用该类的程序员则只需要抽象地思考类型做了什么,而无须了解类型的工作细节。
--引用自《C++ Primer》
在C++中定义抽象数据类型(Abstract Data Type, ADT)的核心是通过类(class)将数据和对数据的操作封装起来,并对外仅暴露接口(API),隐藏实现细节。
private
,避免外部直接修改。以栈(Stack)为例,需支持以下操作:
push
):将元素添加到栈顶。pop
):移除栈顶元素。top
)。isEmpty
)。size
)。在头文件(.h
)中声明类,仅暴露必要的接口:
// Stack.h
#ifndef STACK_H
#define STACK_H
class Stack {
public:
Stack(); // 默认构造函数
~Stack(); // 析构函数
void push(int value); // 压栈
void pop(); // 弹栈
int top() const; // 获取栈顶元素
bool isEmpty() const; // 判断栈是否为空
int size() const; // 获取栈大小
private:
int* data_; // 存储栈元素的数组
int capacity_; // 栈的容量
int topIndex_; // 栈顶索引
void resize(int newCapacity); // 调整栈容量(内部使用)
};
#endif
在源文件(.cpp
)中定义具体实现,隐藏细节:
// Stack.cpp
#include "Stack.h"
#include // 用于抛出异常
Stack::Stack() : data_(new int[4]), capacity_(4), topIndex_(-1) {}
Stack::~Stack() {
delete[] data_; // 释放动态内存
}
void Stack::push(int value) {
if (topIndex_ == capacity_ - 1) {
resize(capacity_ * 2); // 容量不足时扩容
}
data_[++topIndex_] = value;
}
void Stack::pop() {
if (isEmpty()) {
throw std::out_of_range("Stack is empty!");
}
topIndex_--;
}
int Stack::top() const {
if (isEmpty()) {
throw std::out_of_range("Stack is empty!");
}
return data_[topIndex_];
}
bool Stack::isEmpty() const {
return topIndex_ == -1;
}
int Stack::size() const {
return topIndex_ + 1;
}
void Stack::resize(int newCapacity) {
int* newData = new int[newCapacity];
for (int i = 0; i <= topIndex_; i++) {
newData[i] = data_[i];
}
delete[] data_;
data_ = newData;
capacity_ = newCapacity;
}
data_
、capacity_
、topIndex_
)设为private
,防止外部直接访问。push
、pop
等公共方法操作栈。// 在类声明中添加以下代码(C++11+)
Stack(const Stack&) = delete; // 禁用拷贝构造
Stack& operator=(const Stack&) = delete; // 禁用赋值运算符
pop
和top
中检查栈是否为空,抛出std::out_of_range
异常。try-catch
处理异常: #include
int main() {
Stack s;
try {
s.push(10);
std::cout << s.top() << std::endl; // 输出10
s.pop();
s.pop(); // 抛出异常
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
resize
在栈满时自动扩容,用户无需关心内部扩容逻辑。用户只需包含头文件,调用公共接口:
#include "Stack.h"
#include
int main() {
Stack s;
s.push(1);
s.push(2);
s.push(3);
std::cout << "Top: " << s.top() << std::endl; // 3
std::cout << "Size: " << s.size() << std::endl; // 3
s.pop();
std::cout << "Top after pop: " << s.top() << std::endl; // 2
return 0;
}
template
class Stack {
public:
void push(const T& value);
// ... 其他成员
private:
T* data_;
};
#include
private:
std::unique_ptr data_;
Stack(Stack&& other) noexcept; // 移动构造函数
Stack& operator=(Stack&& other) noexcept; // 移动赋值运算符
定义抽象数据类型的关键步骤:
private
。this
关键字:指向当前对象的指针this
是一个隐式指针,指向当前对象的地址。class MyClass {
private:
int value; // 成员变量
public:
// 场景1:同名参数与成员变量区分
void setValue(int value) {
this->value = value; // 使用this访问成员变量
}
// 场景2:返回当前对象的引用(链式调用)
MyClass& increment() {
value++;
return *this; // 返回当前对象的引用
}
// 场景3:比较当前对象与其他对象
bool compare(const MyClass& other) {
return this->value > other.value;
}
};
int main() {
MyClass obj1, obj2;
obj1.setValue(5);
obj1.increment().increment(); // 链式调用:value变为7
bool result = obj1.compare(obj2); // 比较obj1和obj2
return 0;
}
const
成员函数:保证不修改对象状态const
,表示该函数不会修改类的成员变量。const
成员函数只能读取成员变量,不能修改。const
对象(如 const MyClass obj;
)只能调用 const
成员函数。class MyClass {
public:
int getValue() const { // const成员函数
return value;
}
};
class Counter {
private:
int count;
public:
Counter() : count(0) {}
// 非const成员函数:可修改成员变量
void increment() {
count++;
}
// const成员函数:只能读取成员变量
int getCount() const {
return count;
}
// 错误示例:尝试在const成员函数中修改成员变量
/*
void reset() const {
count = 0; // 编译错误!const函数不能修改成员变量
}
*/
};
int main() {
Counter c1;
c1.increment();
cout << c1.getCount(); // 输出1
const Counter c2;
// c2.increment(); // 错误!const对象只能调用const成员函数
cout << c2.getCount(); // 合法,调用const函数
return 0;
}
this
与const
成员函数的关系this
指针的类型const
成员函数中:MyClass* const this
(指针本身不可变,指向的对象可变)。const
成员函数中:const MyClass* const this
(指针和指向的对象都不可变)。this
的const
性class MyClass {
public:
void nonConstFunc() {
this->value = 10; // 允许修改成员变量
}
void constFunc() const {
// this->value = 20; // 错误!const成员函数中this是const指针
int temp = this->value; // 允许读取成员变量
}
};
const
正确性设计原则const
:
const
对象调用这些函数。const
和非const
成员函数:
const
性选择不同实现。class Data {
private:
std::vector data;
public:
// 非const版本:允许修改数据
std::vector& getData() { return data; }
// const版本:返回const引用,防止修改
const std::vector& getData() const { return data; }
};
this
关键字:
const
成员函数:
const
对象调用,增强代码安全性。const
成员函数中,this
是一个指向const
对象的指针。const
正确性设计,提升代码健壮性。类作用域是指类内定义的成员(变量和函数)的可见性范围。所有成员变量和成员函数都位于类的作用域内,需通过对象或类名(静态成员)访问。
.
运算符)或指针(->
运算符)访问。public
、private
、protected
修饰符影响成员的可见性。int value = 100; // 全局变量
class MyClass {
private:
int value = 10; // 类作用域内的成员变量(与全局变量同名)
public:
void print() {
std::cout << value; // 输出10(访问类内成员)
std::cout << ::value; // 输出100(通过::访问全局变量)
}
};
成员函数是定义在类作用域内的函数,用于操作类对象的状态(成员变量)。
class Calculator {
public:
int add(int a, int b) { return a + b; }
};
::
(适合复杂函数)。 class Calculator {
public:
int multiply(int a, int b); // 声明
};
// 类外定义
int Calculator::multiply(int a, int b) {
return a * b;
}
public
成员函数:可被任何代码调用。private
/protected
成员函数:只能在类内或友元中调用。class BankAccount {
private:
double balance;
void logTransaction(const std::string& msg) { /* 记录日志 */ }
public:
void deposit(double amount) {
balance += amount;
logTransaction("Deposit"); // 合法:类内调用私有函数
}
};
int main() {
BankAccount acc;
acc.deposit(1000);
// acc.logTransaction("Test"); // 错误!私有函数不可外部调用
}
当成员函数访问变量时,按以下顺序查找:
::
显式访问)。int x = 1; // 全局变量
class Test {
private:
int x = 2; // 类作用域变量
public:
void print(int x = 3) { // 参数x(局部作用域)
std::cout << x; // 3(局部变量)
std::cout << this->x; // 2(类成员变量)
std::cout << ::x; // 1(全局变量)
}
};
静态成员函数属于类而非对象,因此:
this
指针。#include
using namespace std;
class Logger {
private:
static int logCount; // 静态成员变量
public:
static void incrementLog() {
logCount++; // 合法:访问静态成员
}
};
int Logger::logCount = 0; // 静态成员初始化
int main() {
Logger::incrementLog(); // 合法:调用静态成员函数
return 0;
}
友元函数或类可以突破类作用域限制,访问私有成员,但仍需注意作用域规则。
class Secret {
private:
int code = 42;
friend void hack(Secret& s); // 声明友元函数
};
// 友元函数定义(全局作用域)
void hack(Secret& s) {
std::cout << s.code; // 合法:友元可访问私有成员
}
this
指针。#include
#include
class Student {
private:
std::string name; // 类作用域变量
int score;
public:
// 构造函数
Student(const std::string& n, int s) : name(n), score(s) {}
// 类内定义的成员函数(隐式内联)
void printBasicInfo() {
std::cout << "Name: " << name << std::endl;
}
// 类外定义的成员函数
void printGrade();
// 静态成员函数
static void printSchool() {
std::cout << "MIT" << std::endl;
}
};
// 类外定义成员函数
void Student::printGrade() {
if (score >= 90) std::cout << "A";
else std::cout << "B";
}
int main() {
Student alice("Alice", 95);
alice.printBasicInfo(); // 输出Name: Alice
alice.printGrade(); // 输出A
Student::printSchool(); // 输出MIT
return 0;
}
非成员函数是指不属于任何类的函数,但与类有逻辑关联,用于操作或扩展类的功能。
核心特点:
<<
, +
, ==
)当运算符需要支持对称操作(如 a + b
与 b + a
)时,非成员函数更灵活。
class Complex {
public:
Complex(double real, double imag) : real_(real), imag_(imag) {}
double real() const { return real_; }
double imag() const { return imag_; }
private:
double real_, imag_;
};
// 非成员函数:重载加法运算符
Complex operator+(const Complex& a, const Complex& b) {
return Complex(a.real() + b.real(), a.imag() + b.imag());
}
// 使用
Complex c1(1, 2), c2(3, 4);
Complex c3 = c1 + c2; // 调用operator+
<<
输出)#include
// 非成员函数:重载<<运算符
std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << "(" << c.real() << ", " << c.imag() << ")";
return os;
}
// 使用
std::cout << c3; // 输出 (4, 6)
提供与类相关的辅助功能,但无需依赖私有数据。
// 非成员工具函数:计算复数的模
double magnitude(const Complex& c) {
return std::sqrt(c.real() * c.real() + c.imag() * c.imag());
}
// 使用
double mod = magnitude(c3);
封装对象创建的复杂逻辑。
// 非成员工厂函数
Complex createFromPolar(double r, double theta) {
return Complex(r * cos(theta), r * sin(theta));
}
// 使用
Complex c4 = createFromPolar(5.0, M_PI / 4);
class Complex {
// 声明友元函数
friend Complex operator*(const Complex& a, const Complex& b);
private:
double real_, imag_;
};
// 友元函数可访问私有成员
Complex operator*(const Complex& a, const Complex& b) {
return Complex(
a.real_ * b.real_ - a.imag_ * b.imag_,
a.real_ * b.imag_ + a.imag_ * b.real_
);
}
特性 | 非成员函数 | 成员函数 |
---|---|---|
定义位置 | 类外部 | 类内部 |
访问权限 | 仅能访问公有成员(除非声明为友元) | 可访问所有成员(包括私有) |
运算符重载对称性 | 更好(如 a + b 和 b + a ) |
需要第一个操作数是类的对象 |
封装性 | 高(减少类之间的依赖) | 低(与类紧密绑定) |
class Date {
public:
Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
bool isLeapYear() const; // 成员函数
int year() const { return year_; }
int month() const { return month_; }
int day() const { return day_; }
private:
int year_, month_, day_;
};
// 非成员函数:判断两个日期是否连续
bool isConsecutive(const Date& a, const Date& b) {
// 只能通过公有接口访问数据
if (a.year() == b.year() && a.month() == b.month() && a.day() + 1 == b.day()) {
return true;
}
// 处理跨月、跨年等复杂逻辑...
return false;
}
构造函数是类的一种特殊成员函数,用于初始化对象的状态(成员变量)。
核心特性:
void
)。class MyClass {
public:
MyClass() { // 默认构造函数
value = 0;
std::cout << "默认构造函数调用\n";
}
private:
int value;
};
int main() {
MyClass obj; // 调用默认构造函数
return 0;
}
class Point {
public:
Point(int x, int y) { // 参数化构造函数
this->x = x;
this->y = y;
}
private:
int x, y;
};
int main() {
Point p(3, 4); // 调用参数化构造函数
return 0;
}
const
成员、引用成员、没有默认构造函数的类成员。class Circle {
public:
Circle(double r, int color)
: radius(r), color(color) { // 初始化列表
// 函数体可留空
}
private:
const double radius; // const成员必须用初始化列表
int color;
};
const
引用)。class String {
public:
String(const String& other) { // 拷贝构造函数
size = other.size;
data = new char[size];
std::memcpy(data, other.data, size);
}
private:
char* data;
int size;
};
&&
),转移资源而非复制。class String {
public:
String(String&& other) noexcept // 移动构造函数
: data(other.data), size(other.size) {
other.data = nullptr; // 置空原对象指针
other.size = 0;
}
private:
char* data;
int size;
};
class Date {
public:
Date() : Date(2024, 1, 1) {} // 委托给三参数构造函数
Date(int year, int month, int day)
: year(year), month(month), day(day) {}
private:
int year, month, day;
};
MyClass obj; // 隐式调用默认构造函数
new
或临时对象语法。 MyClass* p = new MyClass(); // 显式调用
String s1("Hello");
String s2 = s1; // 调用拷贝构造函数
std::move
显式转移资源。explicit
关键字:禁止隐式类型转换。
class MyClass {
public:
explicit MyClass(int x) { /* ... */ }
};
MyClass obj = 5; // 错误:禁止隐式转换
MyClass obj(5); // 正确:显式调用
深拷贝与浅拷贝:拷贝构造函数需处理动态内存,避免内存泄漏。
资源管理:构造函数中分配的资源应在析构函数中释放。
#include
#include
class DynamicArray {
public:
// 默认构造函数
DynamicArray() : size(0), data(nullptr) {}
// 参数化构造函数
explicit DynamicArray(int size) : size(size) {
data = new int[size];
std::memset(data, 0, size * sizeof(int));
}
// 拷贝构造函数(深拷贝)
DynamicArray(const DynamicArray& other) : size(other.size) {
data = new int[size];
std::memcpy(data, other.data, size * sizeof(int));
}
// 移动构造函数(C++11+)
DynamicArray(DynamicArray&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 析构函数
~DynamicArray() {
delete[] data;
}
private:
int* data;
int size;
};
int main() {
DynamicArray arr1(5); // 参数化构造函数
DynamicArray arr2 = arr1; // 拷贝构造函数
DynamicArray arr3 = std::move(arr1); // 移动构造函数
return 0;
}
拷贝控制成员(拷贝构造函数、拷贝赋值运算符、析构函数)共同管理对象的资源生命周期,是编写健壮C++类的关键。
成员函数 | 触发场景 | 默认行为 | 核心职责 |
---|---|---|---|
拷贝构造函数 | 用同类型对象初始化新对象 | 浅拷贝(成员逐一复制) | 深拷贝资源,避免共享 |
拷贝赋值运算符 | 将同类型对象赋值给已有对象 | 浅拷贝 | 释放旧资源,深拷贝新值 |
析构函数 | 对象销毁时 | 无操作 | 释放所有分配的资源 |
class MyClass {
public:
// 拷贝构造函数(参数为const引用)
MyClass(const MyClass& other) {
// 深拷贝逻辑
}
};
MyClass obj2(obj1);
或 MyClass obj2 = obj1;
class DynamicArray {
public:
int* data;
int size;
// 拷贝构造函数
DynamicArray(const DynamicArray& other) : size(other.size) {
data = new int[size];
std::copy(other.data, other.data + size, data);
}
};
class MyClass {
public:
// 拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
if (this != &other) { // 处理自赋值
// 释放旧资源 + 深拷贝新资源
}
return *this;
}
};
obj2 = obj1;
obj3 = obj2 = obj1;
DynamicArray& operator=(const DynamicArray& other) {
if (this != &other) {
delete[] data; // 释放旧内存
size = other.size;
data = new int[size]; // 申请新内存
std::copy(other.data, other.data + size, data);
}
return *this;
}
class MyClass {
public:
~MyClass() {
// 释放资源(动态内存、文件句柄等)
}
};
delete ptr;
vector
析构时~DynamicArray() {
delete[] data; // 释放动态数组
data = nullptr;
}
原则:如果一个类需要自定义拷贝构造函数、拷贝赋值运算符或析构函数中的任意一个,则通常需要自定义全部三个。
class String {
private:
char* data;
size_t length;
public:
// 1. 析构函数
~String() { delete[] data; }
// 2. 拷贝构造函数
String(const String& other) : length(other.length) {
data = new char[length + 1];
strcpy(data, other.data);
}
// 3. 拷贝赋值运算符
String& operator=(const String& other) {
if (this != &other) {
delete[] data; // 关键:释放旧资源
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
return *this;
}
};
新增移动构造函数和移动赋值运算符,适用于资源转移优化:
class String {
public:
// 移动构造函数
String(String&& other) noexcept
: data(other.data), length(other.length) {
other.data = nullptr; // 置空原对象
other.length = 0;
}
// 移动赋值运算符
String& operator=(String&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
return *this;
}
};
a = a
的情况。class DynamicArray {
public:
// 构造函数
DynamicArray(int size) : size(size), data(new int[size]) {}
// 1. 析构函数
~DynamicArray() { delete[] data; }
// 2. 拷贝构造函数
DynamicArray(const DynamicArray& other) : size(other.size) {
data = new int[size];
std::copy(other.data, other.data + size, data);
}
// 3. 拷贝赋值运算符
DynamicArray& operator=(const DynamicArray& other) {
if (this != &other) {
int* newData = new int[other.size]; // 先分配新资源
std::copy(other.data, other.data + other.size, newData);
delete[] data; // 再释放旧资源
data = newData;
size = other.size;
}
return *this;
}
private:
int* data;
int size;
};
访问控制通过三个关键字实现:
public
、private
、protected
,它们决定了类成员的可见性范围。
public
成员private
成员friend
)可访问。protected
成员class BankAccount {
public:
// 公有接口:暴露给用户的API
BankAccount(const std::string& owner, double balance)
: owner_(owner), balance_(balance) {}
void deposit(double amount) {
if (amount > 0) balance_ += amount;
}
bool withdraw(double amount) {
if (amount > 0 && balance_ >= amount) {
balance_ -= amount;
return true;
}
return false;
}
double getBalance() const {
return balance_;
}
private:
// 私有数据:外部无法直接操作
std::string owner_;
double balance_;
};
int main() {
BankAccount acc("Alice", 1000);
acc.deposit(500); // 合法:调用公有接口
// acc.balance_ = 10000; // 错误!无法直接访问私有成员
return 0;
}
关键点:
deposit
和withdraw
:通过公有接口控制对balance_
的修改,确保业务规则(如金额有效性检查)。getBalance
:提供只读访问,防止数据被意外修改。class
关键字:默认成员为private
。struct
关键字:默认成员为public
。class MyClass { // 默认private
int x; // private
};
struct MyStruct { // 默认public
int y; // public
};
friend
)突破封装class Secret {
private:
int code = 42;
friend void hackSecret(Secret& s); // 声明友元函数
};
// 友元函数可以访问私有成员
void hackSecret(Secret& s) {
std::cout << s.code << std::endl; // 合法
}
protected
访问权限的合理使用class Shape {
protected:
// 派生类可访问,外部不可访问
double x_, y_; // 形状的坐标
public:
Shape(double x, double y) : x_(x), y_(y) {}
virtual double area() const = 0; // 纯虚函数
};
class Circle : public Shape {
private:
double radius_;
public:
Circle(double x, double y, double r)
: Shape(x, y), radius_(r) {}
double area() const override {
return 3.14159 * radius_ * radius_;
}
};
int main() {
Circle c(0, 0, 5);
// c.x_ = 10; // 错误!protected成员只能在派生类内部访问
return 0;
}
class Person {
private:
int age_;
public:
void setAge(int age) {
if (age >= 0) age_ = age; // 验证逻辑
}
int getAge() const { return age_; }
};
friend
会破坏封装性。public
、private
、protected
实现数据保护。public
:定义用户交互的API。private
:隐藏实现细节和数据。protected
:支持继承体系中的共享逻辑。const
成员函数、移动语义等特性,构建更安全的类。友元关系是C++中一种允许特定外部类或函数访问另一个类的私有(private)或保护(protected)成员的机制。通过friend
关键字声明,友元能够突破封装性,适用于需要紧密协作的场景,但需谨慎使用以避免破坏代码结构。
友元函数
class MyClass {
friend void friendFunction(MyClass& obj); // 友元函数声明
private:
int secret;
};
void friendFunction(MyClass& obj) {
obj.secret = 42; // 允许访问私有成员
}
友元类
class FriendClass;
class MyClass {
friend class FriendClass; // 友元类声明
private:
int secret;
};
class FriendClass {
public:
void modifySecret(MyClass& obj) {
obj.secret = 100; // 允许访问私有成员
}
};
类的成员函数作为友元
class OtherClass {
public:
void specialAccess(MyClass& obj);
};
class MyClass {
friend void OtherClass::specialAccess(MyClass& obj); // 成员函数友元声明
private:
int secret;
};
void OtherClass::specialAccess(MyClass& obj) {
obj.secret = 200; // 允许访问
}
单向性
友元关系是单向的。若类A声明类B为友元,类B可访问A的私有成员,但类A不能自动访问类B的私有成员。
非传递性
友元关系不可传递。若类A是类B的友元,类B是类C的友元,类A不自动成为类C的友元。
不受访问控制符限制
友元声明可置于类的任何区域(public、private或protected),不影响其权限。
不可继承
派生类不继承基类的友元关系。若基类Base有友元类Friend,派生类Derived的友元不包括Friend。
运算符重载
重载输入输出运算符<<
和>>
时,常需声明为友元以访问私有数据。
class MyClass {
int data;
friend std::ostream& operator<<(std::ostream& os, const MyClass& obj);
public:
MyClass(int d) : data(d) {}
};
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
os << obj.data; // 访问私有成员
return os;
}
紧密协作的类
如树(Tree)与节点(Node)类,相互访问内部结构。
class Tree; // 前向声明
class Node {
int value;
Tree* parentTree;
friend class Tree; // Tree可访问Node的私有成员
};
class Tree {
Node* root;
public:
void updateRoot(Node* newRoot) {
newRoot->parentTree = this; // 访问Node的私有成员
root = newRoot;
}
};
日志或调试工具类
日志类需要访问其他类的私有数据以记录详细信息。
class BankAccount {
double balance;
friend class Logger; // Logger可访问余额
public:
BankAccount(double b) : balance(b) {}
};
class Logger {
public:
static void logBalance(const BankAccount& acc) {
std::cout << "Current balance: " << acc.balance << std::endl;
}
};
避免过度使用
过度依赖友元会破坏封装性,增加代码耦合度。优先考虑通过公有接口(如Getter/Setter)实现功能。
替代方案评估
在以下场景中,可能不需要友元:
模板类中的友元
模板类声明友元时需注意模板参数的匹配。
template
class Box {
T content;
friend void peek(const Box& box) { // 每个模板实例生成对应友元函数
std::cout << box.content << std::endl;
}
};
安全性与异常处理
友元函数或类可能修改私有数据,需确保操作的安全性,如参数验证和异常处理。
在C++中,聚合类(Aggregate Class) 是一种特殊类型的类,允许通过初始化列表(花括号 {}
)直接初始化其成员。
一个类必须满足以下所有条件,才能被视为聚合类:
public
private
)或保护(protected
)的非静态成员。聚合初始化
可以直接通过初始化列表 {}
按顺序初始化所有成员,无需定义构造函数。
struct Point { // 聚合类
int x;
int y;
};
Point p1 = {1, 2}; // 聚合初始化
Point p2{3, 4}; // C++11起支持直接初始化
支持嵌套聚合初始化
如果聚合类的成员本身也是聚合类型,可以嵌套使用 {}
初始化。
struct Rectangle { // 聚合类
Point topLeft;
Point bottomRight;
};
Rectangle rect = {{0, 0}, {10, 20}};
允许默认成员初始化(C++11+)
聚合类的成员可以指定默认值,但初始化列表仍按顺序覆盖默认值。
struct Circle {
int x = 0; // 默认值
int y = 0;
int radius = 1;
};
Circle c1; // 使用默认值:x=0, y=0, radius=1
Circle c2 = {5, 5, 10}; // 覆盖默认值:x=5, y=5, radius=10
Circle c3 = {5, 5}; // 部分覆盖:x=5, y=5, radius=1(保留默认)
初始化列表必须严格按顺序
成员的初始化顺序必须与类中声明的顺序一致。
struct Data {
int id;
std::string name;
};
Data d1 = {42, "Alice"}; // 正确
Data d2 = {"Bob", 10}; // 错误!类型和顺序不匹配
不能跳过成员初始化
必须为所有成员提供初始化值,或依赖默认成员初始化(C++11+)。
struct Person {
std::string name;
int age = 0; // 默认值
};
Person p1 = {"Alice"}; // 正确:age使用默认值0
Person p2 = {"Bob", 30}; // 正确:age被覆盖为30
Person p3 = {}; // 正确:name初始化为空字符串,age=0
不允许窄化转换
初始化列表中不能发生窄化转换(如 double
→ int
需要显式转换)。
struct Value {
int x;
};
Value v1 = {3.14}; // 错误!double→int是窄化转换
Value v2 = {static_cast(3.14)}; // 正确
结构体(struct
)
默认所有成员为 public
,天然适合作为聚合类。
struct Vec3 { // 聚合类
float x, y, z;
};
类(class
)
若显式将所有成员设为 public
,且满足其他条件,也可作为聚合类。
class RGB { // 聚合类
public:
int r, g, b;
};
RGB color = {255, 128, 64};
数据容器(POD类型)
用于存储简单数据,如坐标、颜色、配置参数等。
struct Config { // 配置文件参数
int width;
int height;
bool fullscreen;
};
Config config = {1920, 1080, true};
与C语言兼容的结构体
在混合C/C++编程时,确保数据布局兼容。
extern "C" {
struct CCompatibleStruct { // 可被C代码使用
int a;
double b;
};
}
序列化与反序列化
便于将聚合类转换为字节流或JSON格式。
#include
using json = nlohmann::json;
struct User {
std::string username;
int age;
};
User u = {"Alice", 30};
json j = u; // 自动序列化为 {"username": "Alice", "age": 30}
特性 | 聚合类 | 非聚合类 |
---|---|---|
构造函数 | 无用户声明 | 可有用户声明构造函数 |
数据成员访问权限 | 所有非静态成员为 public |
可包含 private /protected |
初始化方式 | 直接使用 {} 初始化列表 |
需调用构造函数 |
继承关系 | 无基类 | 可有基类 |
struct
)定义聚合类。字面值常量类(Literal Class),这是C++11引入的特性,允许用户自定义类型在编译期进行常量计算。
一个类要成为字面值常量类,必须满足以下条件:
int
、double
、其他字面类等)。constexpr
构造函数。=default
)。class Distance {
private:
double meters; // 数据成员必须是字面值类型
public:
// 步骤2:添加constexpr构造函数
constexpr Distance(double m) : meters(m) {}
// 步骤3:定义constexpr成员函数(可选)
constexpr double toKilometers() const {
return meters / 1000.0;
}
// 步骤4:析构函数必须默认(可省略,编译器自动生成)
~Distance() = default;
};
double
、int
、其他字面类等。constexpr
构造函数constexpr
修饰。constexpr Distance(double m) : meters(m) {}
constexpr
成员函数(可选)constexpr double toKilometers() const {
return meters / 1000.0;
}
constexpr Distance d1(500.0); // 编译期初始化
constexpr double km = d1.toKilometers(); // 编译期计算:0.5 km
template
void printDistance() {
std::cout << D.toKilometers() << " km\n";
}
int main() {
printDistance(); // 输出 2 km
return 0;
}
#include
class Distance {
private:
double meters;
public:
constexpr Distance(double m) : meters(m) {}
constexpr double toKilometers() const {
return meters / 1000.0;
}
constexpr Distance operator+(const Distance& other) const {
return Distance(meters + other.meters);
}
};
// 用户定义的字面量(C++11)
constexpr Distance operator"" _m(long double m) {
return Distance(m);
}
constexpr Distance operator"" _km(long double km) {
return Distance(km * 1000.0);
}
int main() {
constexpr Distance d1 = 500.0_m; // 500米
constexpr Distance d2 = 2.5_km; // 2500米
constexpr Distance total = d1 + d2; // 3000米
// 编译期计算
constexpr double km = total.toKilometers(); // 3.0 km
std::cout << "Total distance: " << km << " km\n";
return 0;
}
constexpr
构造函数、字面类型成员、平凡析构。class Counter {
public:
static int count; // 声明静态成员变量
Counter() { count++; }
~Counter() { count--; }
};
静态成员变量必须在类外单独定义(分配内存)。
int Counter::count = 0; // 初始化,不写static关键字
Counter obj1, obj2;
cout << Counter::count; // 通过类名访问(推荐)
class MathUtils {
public:
static double pi() { // 静态成员函数
return 3.1415926;
}
static int add(int a, int b); // 声明
};
// 类外定义
int MathUtils::add(int a, int b) {
return a + b;
}
double pi = MathUtils::pi(); // 无需对象
MathUtils utils;
int sum = utils.add(3, 5); // 允许但不推荐
this
指针。class InstanceCounter {
public:
static int instanceCount;
InstanceCounter() { instanceCount++; }
~InstanceCounter() { instanceCount--; }
};
int InstanceCounter::instanceCount = 0;
int main() {
InstanceCounter a, b;
cout << InstanceCounter::instanceCount; // 输出2
return 0;
}
class Logger {
private:
static std::ofstream logFile; // 共享日志文件
public:
static void log(const std::string& message) {
logFile << message << std::endl;
}
};
std::ofstream Logger::logFile("app.log");
class StringUtils {
public:
static std::string toUpper(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
};
const
整型、枚举或字面值类型。class Settings {
public:
static const int MAX_USERS = 100; // 类内初始化
static constexpr double VERSION = 2.0; // C++11 constexpr
};
// 类外定义(仍需提供,但不赋值)
const int Settings::MAX_USERS;
constexpr double Settings::VERSION;
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // C++11起线程安全
return instance;
}
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
private:
Singleton() {} // 私有构造函数
};
class Derived : access-specifier Base {
// 派生类成员
};
public
、protected
、private
,决定基类成员在派生类中的访问权限。class Animal {
public:
void eat() { cout << "Eating" << endl; }
};
class Dog : public Animal {
public:
void bark() { cout << "Barking" << endl; }
};
Dog d;
d.eat(); // 继承自Animal
d.bark();
class Shape {
public:
virtual double area() const { return 0.0; } // 虚函数
virtual ~Shape() {} // 虚析构函数(重要!)
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
};
class AbstractShape {
public:
virtual void draw() const = 0; // 纯虚函数
};
class Circle : public AbstractShape {
public:
void draw() const override { /* 实现 */ }
};