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];
		}
相关推荐
烬羽9 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
郝学胜_神的一滴9 小时前
CMake 034:生成器表达式:解耦构建时序、精简分支逻辑的终极利器
c++·cmake
云技纵横9 小时前
一个 @Async,把 @Transactional 的事务边界打穿了
后端·面试
想要成为糕糕手9 小时前
Harness Engineering:大模型时代的“马鞍”——从记忆层开始,让AI真正为你所用
面试·ai编程·claude
kyriewen1 天前
我手写了一个 EventEmitter,面试官追问了 6 个问题——第 4 个我没答上来
前端·javascript·面试
见过夏天1 天前
C++ 基础入门完全指南
c++
她的男孩1 天前
后台接口加密别只会 HTTPS,ForgeAdmin 的 RSA + SM4/AES 源码拆解
后端·面试·开源
Randyliu1 天前
20260508-Agent搭建记录以及对ReAct框架的理解
面试·agent
ZzT1 天前
公司用 AI 筛简历,他写了个 AI 帮你挑公司
面试·aigc·ai编程
PBitW1 天前
GPT训练我的第四天,被打惨了!!!😭😭😭
前端·javascript·面试