.
C++中的接口实现有 function接口、class接口和template接口等,每一种接口都是用户与代码互动的方式。为了开发一个“容易被正确使用,不容易被误用”的接口,有如下几种方式:
1、使用外覆类型增强使用规范
- 如下Date的构造函数设计,与错误使用
class Date{
public:
Date(int month,int day,int year);
...
};
int main(){
Data d(30,3,1995); // 本来想输入的是3月30号,但却传递了一个无效的日期
return 0;
}
- 外覆类型通过为month,day和year分别创建不同的类型,保证在编译时规避错误,同时让代码更易读和理解。
- explicit关键字,可防止隐式转换
#include <iostream>
#include <stdexcept>
// 定义外覆类型
struct Month {
explicit Month(int m) : val(m) {
if (m < 1 || m > 12) {
throw std::invalid_argument("Month must be between 1 and 12");
}
}
int val;
};
struct Day {
explicit Day(int d) : val(d) {
if (d < 1 || d > 31) {
throw std::invalid_argument("Day must be between 1 and 31");
}
}
int val;
};
struct Year {
explicit Year(int y) : val(y) {}
int val;
};
// Date 类
class Date {
public:
Date(Month month, Day day, Year year)
: month_(month), day_(day), year_(year) {}
void print() const {
std::cout << "Date: " << month_.val << "/" << day_.val << "/" << year_.val << std::endl;
}
private:
Month month_;
Day day_;
Year year_;
};
int main() {
try {
// 使用外覆类型创建 Date 对象
Date date(Month(10), Day(25), Year(2023));
date.print();
Date date(Month(13), Day(25), Year(2023)); // 抛出异常,月份无效
} catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
2、使用枚举类型
- 通过menu class代替普通的emnu,避免枚举值的隐式转换和命名冲突
enum class Color { Red, Green, Blue };
void setColor(Color color) {
switch (color) {
case Color::Red: std::cout << "Red\n"; break;
case Color::Green: std::cout << "Green\n"; break;
case Color::Blue: std::cout << "Blue\n"; break;
}
}
int main() {
setColor(Color::Red); // 正确
// setColor(0); // 错误:无法通过编译
}
3、使用资源管理类
- 使用智能指针std::unique_ptr和std::shared_ptr来管理动态内存,避免函数返回值的内存泄漏和悬空指针
std::unique_ptr<int> createInt(int value) {
return std::make_unique<int>(value);
}
std::shared_ptr<int> createSharedInt(int value) {
return std::make_shared<int>(value);
}
int main() {
auto ptr = createInt(42);
auto sharedPtr = createSharedInt(42);
}
4、使用RAII技术
- RAII方面用于资源管理的技术,如内存,文件句柄等,通过对象的生命周期来管理资源的获取和释放,避免资源泄漏。
class File {
public:
File(const std::string& filename) : file_(fopen(filename.c_str(), "r")) {
if (!file_) throw std::runtime_error("Failed to open file");
}
~File() { if (file_) fclose(file_); }
FILE* get() const { return file_; }
private:
FILE* file_;
};
void readFile(const std::string& filename) {
File file(filename);
// 使用 file.get() 读取文件
}
思维导图笔记: