string类从编译链接到构造析构,解决多文件重复定义+运算符重载,面试必考!

5月26日每日学习

文章目录

string的实现

0.类中的private

cpp 复制代码
namespace lcj
{
	class string
	{
	public:


	private:
		size_t _capacity; 
		char* _str;
		size_t _size; 
	};
}

1.头文件定义函数导致的多文件重复定义问题

是啥:

函数定义只能出现一次。那么,如果我把++用来测试的函数++ 定义 放在 .h文件中,

------> 两个.c文件都有这个头文件,非法

原因:编译链接的过程 :

  1. 预处理 :把 #include 的头文件内容原封不动地复制 到每个 .cpp 文件里。
  2. 编译 :分别把 a.cppb.cpp 编译成 目标文件 a.obj / a.ob.obj / b.o
    此时 a.obj 里记录了"我定义了 sayHello",b.obj 里也记录了"我定义了 sayHello"。
  3. 链接 :把多个目标文件合并 成一个可执行文件。
    链接器看到 a.objb.obj 都定义了一个叫 sayHello 的函数 ,按照 C++ 的单一定义规则(ODR, One Definition Rule) ,这是非法的,于是报错

解决方法

  1. 使用inline内联:链接器允许程序中有多个相同的 inline 函数定义,只要它们完全相同。链接器会随便选一个,或者全部丢弃,不会报错。
  2. 静态static成员函数:

静态成员函数

唯一区别:没有this指针

限制:

  • ++不能访问普通成员变量和成员函数++(因为没有this指针)
  • 只能访问静态(static)变量和函数

使用上:

  1. 普通成员函数(唯一使用方法 ):a1.GetCount(); ( 有this指针)

  2. 静态成员函数(成员变量也可以):

    1. 直接用:A::GetCount();(✅ 推荐,更方便)(成员变量也可以

    2. 先创建类对象再用:

      A a1;

      a1.GetCount(); (没有this指针,但能找到类域)

⬇️静态成员变量,所有同类对对象共享------>用来计数

  1. 最标准的方法:声明和定义分离

MyString.h

cpp 复制代码
#pragma once
#include<iostream>
using namespace std;

namespace lcj
{
	class string
	{
	public: ...... 
	private: ...... 
	};



	void test_string1();
    
    
}

MyString.cpp

cpp 复制代码
#include"MyString.h"
namespace lcj
{
	void test_string1()
	{
		string s1;
		string s2("hello world");
	}
}

test.cpp

cpp 复制代码
#include"MyString.h"

int main() 
{
    lcj::test_string1();
    return 0;
}





2.构造函数

2.1带参构造函数的坑点

❌看似正确:

cpp 复制代码
		string(const char* s)
			:_size(strlen(s))
			, _str(new char[_size + 1])
		{ 
         }


	private:
		size_t _capacity; 
		char* _str;
		size_t _size; 

‼️错误点:初始化列表的 初始化顺序 是 按 声明顺序不是初始化列表顺序‼️

------>_str(new char[_size + 1])中的:_size随机数

为什么不建议在初始化列表中定义:

  1. 上面说的,_str(new char[_size + 1])中的:_size 可能因为声明顺序的原因,是随机数
  2. 即使改变了声明顺序,但,如果有人改了代码,就完了

正确的方式

------>建议在函数体内直接定义

cpp 复制代码
		string(const char* s) 
		{
			_size = strlen(s);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, s );
		}

	private:
		size_t _capacity; 
		char* _str;
		size_t _size; 





2.2默认构造函数

❌看似正确:但是,没法打印------打印时候对空指针解引用

cpp 复制代码
		string()
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{

		}

正确写法:

cpp 复制代码
		string()
			:_str(new char[1]{'\0'})//得写成这样,才能像数组一样操作
			, _size(0)
			, _capacity(0)
		{

		}





2.3合一 ------上面两个构造函数

cpp 复制代码
		string(const char* s = "")//默认有一个'\0'
		{
			_size = strlen(s);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, s);//strcpy也会加上'\0'
		}





3.析构函数

cpp 复制代码
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}





4.size

cpp 复制代码
		size_t size()
		{
			return _size;
		}



5.operator

➡️const修饰类的成员函数的知识在这里⬅️

➡️运算符重载的知识在这里⬅️

  • 作用:能直接通过下标访问string中的字符
  • 返回值:对应字符的引用

两个函数

  1. char& operator[] (size_t pos);
  2. const char& operator[] (size_t pos) const;
  • const char& operator[](size_t pos) const为啥末尾有const
    • 允许 const 对象调用 :如果一个 string 对象被声明为 const,那么它只能调用那些被 const 修饰的成员函数。没有末尾 constoperator[] 版本无法被 const 对象使用。
cpp 复制代码
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}


		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
相关推荐
swipe9 分钟前
做多轮对话 Agent,为什么我建议把短期记忆放到 Redis
后端·面试·llm
宋拾壹34 分钟前
同时添加多个类目
android·开发语言·javascript
swipe44 分钟前
别再把关系库和向量库拆开了:PostgreSQL 搭建 AI 长期记忆层实战
面试·langchain·llm
凡人叶枫1 小时前
Effective C++ 条款04:确定对象被使用前已先被初始化
java·linux·开发语言·c++·嵌入式开发
不想写代码的星星1 小时前
std::move 根本不移动,就像老婆饼里没有老婆
c++
redaijufeng1 小时前
C++雾中风景7:闭包
c++·算法·风景
小小龙学IT1 小时前
Go 语言后端开发:从并发模型到生产落地的工程实践
开发语言·后端·golang
神奇小汤圆2 小时前
将 Pi Agent 接入 HagiCode 的实践之路
面试
ytttr8732 小时前
Qt 数字键盘实现
开发语言·qt
wearegogog1232 小时前
C# .NET 文件比较工具 WinForms
开发语言·c#·.net