一、C++友元函数和友元类
在C++中,一个类的成员有3种属性,分别是public、private、protected。其中只有public成员才能被对象访问,只有本类中的函数可以访问本类的private成员。
- 友元
那如果其他类的成员函数或者全局范围内的函数想访问当前类中的private成员,该怎么办呢? 在C++中,为了解决上述问题,引入了友元的概念,对应关键字friend,通过友元可以访问与其为好友关系的类中的私有成员。 2.友元函数 友元函数:在当前类以外定义的、不属于当前类的函数也可以在类中声明,但要在前面加上friend关键字修饰。 注意: a. 友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。 b. 友元函数可以访问当前类中所有成员,包括public、protected、private属性的成员。 c. 友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
#include<iostream>
using namespace std;
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);
friend void Stu::Out(Address *addr);
private:
char *m_city;
};
Address::Address(char *city):m_city(city){
cout<<"Address类的构造函数调用结束"<<endl;
}
Stu::Stu(char *name, int age,int weight):m_name(name),m_age(age),m_weight(weight){
cout<<"构造函数结束,初始化完毕"<<endl;
}
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);
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;
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);
friend class Stu;
private:
char *m_city;
};
Address::Address(char *city):m_city(city){
cout<<"Address类的构造函数调用结束"<<endl;
}
Stu::Stu(char *name, int age,int weight):m_name(name),m_age(age),m_weight(weight){
cout<<"构造函数结束,初始化完毕"<<endl;
}
void Stu::show(Address *addr){
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++中类的理解
- 类其实也是一种作用域,每个类都会定义它自己的作用域。
- 在类的作用域之外,普通的成员只能通过对象(可以是对象本身,也可以是对象指针)来访问。
- 静态成员既可以通过对象访问,又可以通过类访问。
- typedef 定义的类型只能通过类来访问。
- 定义在类的外部成员,我们需要写明成员对应的类名。
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() 函数。
#include <iostream>
#include <string>
using namespace std;
int main(){
int len;
string s1;
string s2 = "c plus plus";
string s3 = s2;
string s4 (5, 's'); \\表示有5个‘s’组成。
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;
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;
int main(){
string s, s1, s2,s3;
s = "0112345123";
s1 = s;
s2 = "123";
s.insert(3,"abc");
cout<<s<<endl;
s.erase(3,3);
cout<<s<<endl;
s.erase(5);
cout<<s<<endl;
s.erase(2,9);
cout<<s<<endl;
s3 = s1.substr(3,3);
cout<<s3<<endl;
int indx;
long int indx2;
indx = s1.find(s2,2);
cout<<indx<<endl;
indx2 = s1.find("267");
cout<<indx2<<endl;
indx2 = s1.rfind("123",5);
cout<<indx2<<endl;
indx2 = s1.find_first_of("234");
cout<<indx2<<endl;
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");
string s2 = s1;
cout << (s1 == s2) << endl;
s1[0] = '6';
cout << "s1 = " << s1 << endl;
cout << "s2 = " << s2 << endl;
cout << (s1 == s2) << endl;
return 0;
}
|