IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习杂记(五) -> 正文阅读

[C++知识库]C++学习杂记(五)


一、C++友元函数和友元类

在C++中,一个类的成员有3种属性,分别是public、private、protected。其中只有public成员才能被对象访问,只有本类中的函数可以访问本类的private成员。

  1. 友元
    那如果其他类的成员函数或者全局范围内的函数想访问当前类中的private成员,该怎么办呢?
    在C++中,为了解决上述问题,引入了友元的概念,对应关键字friend,通过友元可以访问与其为好友关系的类中的私有成员。
    2.友元函数
    友元函数:在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加上friend关键字修饰。
    注意:
    a. 友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。
    b. 友元函数可以访问当前类中所有成员,包括public、protected、private属性的成员。
    c. 友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
#include<iostream>
using namespace std;

//提前声明Address类
class Address;

class Stu{
public:
	Stu(char *name, int age,int weight);
	//将全局的函数声明为友元函数
	friend void show(Stu *pStu);
	//成员函数,入参为另一个类的对象
public:
	void Out(Address *addr);
private:
	char *m_name;
	int m_age;
	int m_weight;
};

class Address{
public:
	Address(char *city);
	//将Stu类中的成员函数Out()声明为Address类的友元函数,使得Stu类的对象可以访问Address类的私有成员
	friend void Stu::Out(Address *addr);
private:
	char *m_city;
};

//Address类的构造函数定义
Address::Address(char *city):m_city(city){
	cout<<"Address类的构造函数调用结束"<<endl;
}

//Stu类的构造函数定义
Stu::Stu(char *name, int age,int weight):m_name(name),m_age(age),m_weight(weight){
	cout<<"构造函数结束,初始化完毕"<<endl;
}

//Stu类的成员函数
void Stu::Out(Address *addr){
	cout<<"来自城市:"<<addr->m_city<<endl;
}

//定义一个全局范围的函数,即非成员函数作为友元函数
void show(Stu *pStu){
	cout<<pStu->m_name<<"的年龄是:"<<pStu->m_age<<",体重是:"<<pStu->m_weight<<endl;
}

int main(){
	Stu LM("LiMing",16,54);
	Address addr("吉安");
	show(&LM);
	//Stu类的对象调用成员函数Out,入参为Address类的对象。
	LM.Out(&addr);

	Stu *pXH = new Stu("XiaoHong",18,51);
	Address addr2("上海");
	show(pXH);
	pXH->Out(&addr2);

	return 0;
}

运行结果:
在这里插入图片描述
注意:
a.在Address类的定义之前,Stu类就使用了Address类,所以要提前声明Address类。
b.一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员。

3.友元类
友元类:将整个类声明为另一个类的友元。
有元类的特点:友元类中的所有成员函数都是另外一个类的友元函数。
例如:
将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。

#include<iostream>
using namespace std;

//提前声明Address类
class Address;

class Stu{
public:
	Stu(char *name, int age,int weight);
public:
	void show(Address *addr);
private:
	char *m_name;
	int m_age;
	int m_weight;
};

class Address{
public:
	Address(char *city);
	//将Stu类声明为Address类的友元类
	friend class Stu;
private:
	char *m_city;
};

//Address类的构造函数定义
Address::Address(char *city):m_city(city){
	cout<<"Address类的构造函数调用结束"<<endl;
}

//Stu类的构造函数定义
Stu::Stu(char *name, int age,int weight):m_name(name),m_age(age),m_weight(weight){
	cout<<"构造函数结束,初始化完毕"<<endl;
}


void Stu::show(Address *addr){
	//注意Address的成员变量的引用
	cout<<m_name<<"的年龄是:"<<m_age<<",体重是:"<<m_weight<<"来自城市:"<<addr->m_city<<endl;
}

int main(){
	Stu LM("LiMing",16,54);
	Address addr("吉安");
	LM.show(&addr);

	Stu *pXH = new Stu("XiaoHong",18,51);
	Address addr2("上海");
	pXH->show(&addr2);

	return 0;
}

运行结果:
在这里插入图片描述
注意:
a. 友元的关系是单向的而不是双向的。如果声明了类 B 是类 A 的友元类,不等于类 A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。
b. 友元的关系不能传递。如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。
c.除非有必要,一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。

二、对C++中类的理解

  1. 类其实也是一种作用域,每个类都会定义它自己的作用域。
  2. 在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针)来访问。
  3. 静态成员既可以通过对象访问,又可以通过类访问。
  4. typedef 定义的类型只能通过类来访问。
  5. 定义在类的外部成员,我们需要写明成员对应的类名。
//假如Stu是一个类,在类的外部定义类的成员函数需指明类名Stu。
void Stu::show(){}

二、C++中的class和struct到底有什么区别

1.struct在C语言中。只能包含成员变量,不能包含成员函数。但在C++中,struct可以包含成员变量,也可以包含成员函数。
2.在C++中,struct和class的区别:
a. 使用 class 时,类中的成员默认都是 private 属性的;而使用 struct 时,结构体中的成员默认都是 public 属性的。
b. class 继承默认是 private 继承,而 struct 继承默认是 public 继承。
c. class 可以使用模板,而 struct 不能。

三、C++中的字符串

1.在C++中,除了可以使用C语言中的字符数组或字符串指针,还可以用内置的string类。
2.使用string类时,需要包含头文件
3.string 变量可以直接通过赋值操作符=进行赋值。
4.当需要知道字符串长度时,可以调用 string 类提供的 length() 函数。

//定义string变量
#include <iostream>
#include <string>
using namespace std;
int main(){
    int len;
    string s1; //定义时,没有赋值,所以默认是空字符串。
    string s2 = "c plus plus"; //与C语言不同,没有结束标志“\0”
    string s3 = s2;
    string s4 (5, 's'); \\表示有5个‘s’组成。
    //计算字符串的长度
    //由于 string 的末尾没有'\0'字符,所以 length() 返回的是字符串的真实长度,而不是长度 +1。
    len = s4.length();
    
	cout<<s2<<endl;
	cout<<s3<<endl;
	cout<<s4<<endl;
	cout<<len<<endl;
    return 0;
}

运行结果:
在这里插入图片描述
5.将string类的字符串转换成C语言风格的字符串,可以通过string类的转换函数c_str(),该函数返回该字符串的const指针(const char*)。

为了使用C语言中的 fopen() 函数打开文件,必须将 string 字符串转换为C风格的字符串。

#include <iostream>
#include <string>
using namespace std;
int main(){

	string path = "D:\\work\\visual studio 2010\\Projects\\test0525\\1.txt";
    FILE *fp = fopen(path.c_str(), "rt");
    return 0;
}

6.string 字符串的输入输出也可以和普通变量一样使用<<进行输出,使用>>进行输入。

#include <iostream>
#include <string>
using namespace std;
int main(){
    string s;
    cin>>s;  //输入字符串
    cout<<s<<endl;  //输出字符串

    return 0;
}

运行结果:
输入hello world,但最终只打印出hello,原因是输入运算符>>默认会忽略空格,遇到空格就认为输入结束。
在这里插入图片描述
7.访问字符串中的字符
在C++中,string 字符串也可以像C风格的字符串一样按照下标来访问其中的每一个字符。

#include <iostream>
#include <string>
using namespace std;
int main(){
    string s = "hello";
    //遍历字符串中的字符
    for(int i=0,len=s.length(); i<len; i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    //修改索引为3的字符(索引是从0开始计算)
    s[3] = 'm';
    cout<<s<<endl;
    return 0;
}

运行结果:
在这里插入图片描述
8.字符串的拼接
对于string类,可以使用+或+=运算符来直接拼接字符串(或者字符)。

9.string 字符串的增删改查
(1)插入字符串
insert() 函数可以在 string 字符串中指定的位置插入另一个字符串。
(2)删除字符串
erase() 函数可以删除 string 中的一个子字符串。
(3)提取子字符串
substr() 函数用于从 string 字符串中提取子字符串。
(4)字符串查找
find() 函数用于在 string 字符串中查找子字符串出现的位置。函数返回的是子字符串第一次出现在字符串中的起始下标。如果没有查找到子字符串,那么会返回一个无穷大值。
rfind() 函数最多查找到第二个参数处,如果到了第二个参数所指定的下标还没有找到子字符串,则返回一个无穷大值。
find_first_of() 函数用于查找子字符串和字符串共同具有的字符在字符串中首次出现的位置。

#include <iostream>
#include <string>
using namespace std;
//string 字符串的增删改查
int main(){
    string s, s1, s2,s3;
    s = "0112345123";
	s1 = s;
	s2 = "123"; 	//作为子串s2

	//(1)字符串进行增加
	s.insert(3,"abc"); //insert的入参1表示插入的位置索引,入参2为需要插入的字符串
	cout<<s<<endl; //"011abc2345123"

	//(2)删除字符串成
	s.erase(3,3); //erase的第一个入参是删除的位置索引(注意不要越界,取值为[0,length(s)-1]),第二入参是需要删除的长度(该值要是不给,则默然删除从起始索引开始后所有的字符;如果该值越界,则删除长度自动为length(s)-起始索引)。
	cout<<s<<endl; //删除了“abc","0112345123"

	s.erase(5); //从索引为5的地方开始删除后面所有的字符
	cout<<s<<endl; //"01123"

	s.erase(2,9); //从索引为2的地方开始删除后面的字符,删除长度为9,9+2大于字符总长度5,越界,所以默认删除5-2=3,删除长度为3.
	cout<<s<<endl; //"01"

	//(3)提取字符串
	s3 = s1.substr(3,3); //substr第一个入参为字符串的起始下标(越界则抛出异常),第二个入参为提取字符串的长度(越界则提取从索引到原字符串结尾所有的字符)。
	cout<<s3<<endl; //"234"

	//(4)字符串的查找
	int indx;
	long int indx2;
	indx = s1.find(s2,2);//find的第一个入参是需要查找的字符变量,第二个入参是在原字符串是s1中开始查找的索引。find函数最终返回的是字符串第一次出现在原字符串的起始下标。
	cout<<indx<<endl;//2

	indx2 = s1.find("267");//indx2 = -1,find函数第二个入参没给,则默认从索引0开始查找
	cout<<indx2<<endl;//由于找不到,find函数返回一个非常大的脏值,超过数据类型的最大值,所以返回值一般为-1.

	indx2 = s1.rfind("123",5);//rfind函数的第一个入参是需要找的字符串,第二入参是最多查找到第二个参数处。也就是查找范围为【0,第二个入参】。
	cout<<indx2<<endl;//2

	indx2 = s1.find_first_of("234");//find_first_of函数的入参为需要查找的子字符串,返回值为第一次出现子字符串的索引。
	cout<<indx2<<endl;//3
	cin.get();
    return 0;
}

运行结果如下:
在这里插入图片描述

三、了解C++中string的内容实现

1.在C语言中,有两种表示字符串的方法:
(1)用字符数组

char str[10] = "12ab"; //用字符数组表示的字符串可写

(2)字符串常量

char *str = "12ab"; //字符串只能读不能写。

以上两种表示方式,总是以“\0”作为标志结束,且字符串长度为5(需要包含“\0”)。

2.在C++中,string 在内部封装了与内存和容量有关的信息。string会自动根据字符串的大小进行调整容纳它的内存。
3.使用string类可以规避C语言中常见风险:
a. 数组越界;
b. 通过未被初始化或者被赋以错误值的指针来访问数组元紊;
c. 释放了数组所占内存,但是仍然保留了“悬空”指针。
4.在C语言中,每个字符型数组都占据各自的物理存储区。在 C++ 中,独立的几个 string 对象可以占据也可以不占据各自特定的物理存储区。

#include <iostream>
#include <string>
using namespace std;
int main() {
    string s1("12000");
    //复制一份s1
    string s2 = s1;
    cout << (s1 == s2) << endl; // 结果为1
    s1[0] = '6';
    cout << "s1 = " << s1 << endl;  //02000
    cout << "s2 = " << s2 << endl;  //12000,当s1被改写时,s2会独立拷贝,叫做写时复制
    cout << (s1 == s2) << endl;  //结果为0
    return 0;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 10:58:44  更:2021-08-03 11:00:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 0:54:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码