现代c++第四章 简单类的概念

std::string_view 是 C++17 引入的一个非拥有型(non-owning)字符串引用 :它内部只存「一个字符指针 + 一个长度」,不分配内存、不复制内容、也不负责释放。所以 setString(std::string_view value) 的含义是------「我要读一段字符,但这段字符的所有权还在调用方那里」。

它解决什么问题

在 C++17 之前,写一个只读字符串参数的函数通常要在两种写法里选:

cpp 复制代码
void setString(const std::string& value);   // 传字面量/char* 时要构造临时 std::string(一次堆分配+拷贝)
void setString(const char* value);          // 丢掉长度信息,且不能接受 std::string 以外的容器片段

换成 string_view 后,两种调用都零拷贝、零分配:

cpp 复制代码
setString("hello");                 // 字面量,直接指向 .rodata,不构造 string
setString(std::string("world"));    // 临时 string 只提供指针和长度
setString(buf, 5);                  // 从 C 数组取前 5 个字符
setString(s.substr(3, 4));          // 取子串,仍然不分配内存

对 std::string、char 数组、const char*、std::filesystem::path 等都能隐式转换过来,所以它是很好的「只读输入参数」类型。同时它带长度,能安全表示含 \0 的数据,find/substr/compare 这些操作也不产生新分配。

使用它的三个硬约束

1. 不能悬垂------它不延长任何对象的生命周期

cpp 复制代码
std::string_view bad() {
    std::string s = "temp";
    return s;        // 错:s 析构后 view 指向已释放内存
}

void f() {
    std::string_view sv = std::string("abc");  // 错:临时 string 立刻销毁
}

规则很简单:string_view 的生命周期必须短于它所指向的那段字符。

2. 不保证以 '\0' 结尾

sv.data() 只是指向首字符,sv[sv.size()] 是越界(C++23 起 operator[] 才有 assert 之外的明确行为,但仍不保证有终止符)。所以要传给 strlen、fopen、printf("%s") 这类 C 接口时,得先落地成真正的字符串:

cpp 复制代码
std::string owned(sv);      // 复制一份,保证 null 结尾
::fopen(owned.c_str(), "r");

3. 别拿它当成员变量或全局变量

类里存 std::string_view 成员几乎总是隐患(除非是明确的「借用」语义,且文档写清生命周期依赖,比如某些 span 风格的观察者)。setString 内部如果要保存这个值,应该转成 std::string 存起来:

cpp 复制代码
void setString(std::string_view value) {
    m_value = std::string(value);   // 明确复制,之后与调用方解耦
}

顺带几个细节

  • 按值传是对的 :string_view 通常 16 字节(指针+长度),按值传比 const std::string& 更便宜,不需要写成 const std::string_view&。
  • 它是只读视图 :通过它修改内容属于未定义行为(data() 返回 const char*),要改就转成 std::string 或用 std::span<char>。
  • 和 std::span 是同一思路 :span 是「任意连续区间」的视图,string_view 是专门给字符用的、额外带字符串语义(substr、find、比较运算符)。
  • std::string 可以隐式转 string_view,反之不行 :std::string sv = some_view; 需要显式构造 std::string(sv),这是刻意的------避免悄悄产生分配。
  • 异步/回调场景慎用 :把 string_view 存进 lambda 或线程任务里,很容易在真正执行时已经悬垂;这种场合应传 std::string(或 shared_ptr<const std::string>)。

一句话总结:std::string_view 让 setString 能接受任何形式的字符数据而不产生拷贝,代价是函数只借用、不拥有,因此它绝不能把这段字符留到调用结束之后再用。


言归正传:

通过一个表格单元格类来实现

cpp 复制代码
import std;
import spreadsheet_cell;

int main()
{
	SpreadsheetCell myCell, anotherCell;
	myCell.setValue(6);
	anotherCell.setString("12.3");
	std::println("cell 1:{}", myCell.getValue());
	std::println("anothercell 2:{}", anotherCell.getValue());
	

	auto mycell2{std::make_unique<SpreadsheetCell>()};
	mycell2->setValue(6);
	std::println("mysell2 :{}", mycell2->getValue());
	
	SpreadsheetCell mycell3(3);
	std::println("myshell 3: {}", mycell3.getValue());
	return 0;
}
cpp 复制代码
export module spreadsheet_cell;

import std;

export class SpreadsheetCell//表格中的单个单元的元素?
{
public:
    SpreadsheetCell(double initisValue);
    SpreadsheetCell() {};
    SpreadsheetCell(std::string_view initialValue);
    SpreadsheetCell(const SpreadsheetCell& src);
    SpreadsheetCell& operator=(const SpreadsheetCell&rhs);
    void setValue(double value);
    double getValue() const;

	void setString(std::string_view value);
	std::string getString() const;
private:
    std::string doubleToString(double value) const;
	double stringToDouble(std::string_view value) const;
    double m_value{ 0 };
    
};

void SpreadsheetCell::setValue(double value) {
    m_value = value;
}

double SpreadsheetCell::getValue() const {
    return m_value;
}

void SpreadsheetCell::setString(std::string_view value) {
    m_value = stringToDouble(value);
}

std::string SpreadsheetCell::getString() const {
	return doubleToString(m_value);
}

std::string SpreadsheetCell::doubleToString(double value) const {
	return std::to_string(value);
}

double SpreadsheetCell::stringToDouble(std::string_view value) const// 
{
    double num{ 0 };
	std::from_chars(value.data(), value.data() + value.size(), num);
	return num;
}

SpreadsheetCell::SpreadsheetCell(double initisValue)
{
    setValue(initisValue);
}

SpreadsheetCell::SpreadsheetCell(std::string_view initialValue)
{
    setString(initialValue);
}

SpreadsheetCell::SpreadsheetCell(const SpreadsheetCell& src)
{
    m_value = src.getValue();
}

SpreadsheetCell& SpreadsheetCell::operator=(const SpreadsheetCell& rhs)
{
    // TODO: 在此处插入 return 语句
    if (this == &rhs)
    {
        return *this;
    }

    m_value = rhs.m_value;
    return *this;
}
相关推荐
ctlover1 小时前
langchain 中间件
中间件·langchain·1024程序员节
Be for thing3 小时前
PyTorch自动求导Autograd详解|requires_grad、backward、grad、梯度清零
pytorch·学习·1024程序员节
syagain_zsx3 小时前
ODB 与 MySQL:C++ 里的 ORM 实践(类即表、事务铁律与查询封装)
开发语言·数据库·c++·mysql·工具库·1024程序员节
小嘉丶学长3 小时前
Day08 · 字符串(二)
服务器·开发语言·python·学习·1024程序员节
4SAPI4 小时前
电商全链路内容自动化生成大模型 API 生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
运维·人工智能·架构·自动化·1024程序员节
深圳老胡4 小时前
STM32F103 硬件 SPI 与软件 IO 模拟 SPI 最高速度差异详解
笔记·stm32·单片机·代码规范·1024程序员节
CappuccinoRose4 小时前
滚动交互体系
前端·javascript·交互·html5·1024程序员节
画中有画4 小时前
论可观测性架构在分布式系统中的应用
1024程序员节
惊讶的猫5 小时前
慢查询和排查方式(查询日志 + 分析工具 + EXPLAIN)
mysql·1024程序员节