C/C++:02 2/2. 类和对象

文章目录

  • 11. this指针
    • this到底是什么
  • 12. static静态成员变量详解
  • 13. static静态成员函数详解
  • 14. const 成员变量和成员函数(`常成员函数`)
    • const 成员变量
    • const 成员函数
    • 区分const 的位置
  • 15. const 对象
  • 16. 友元函数和友元类(C++ friend 关键字)
    • 1. 友元函数
    • 友元类
  • 18. class和struct的区别
  • 19. String 详解, C++字符串详解
    • 有必要转换为C风格的字符串
    • string字符串的输入输出
    • 字符串的拼接
    • string 字符串的增删改查
      • 插入字符串
      • 删除字符串
      • 提取子字符串
      • 字符串查找


11. this指针

this是C++中的关键字,也是const指针,它指向当前对象,通过它可以访问当前对象的所有成员。

所谓当前对象,是指正在使用的对象。例如对于stu.show(),stu就是当前对象,this就是指向stu。

void Student::setname(char *name){
    this->name = name;
}
void Student::setage(int age){
    this->age = age;
}
void Student::setscore(float score){
    this->score = score;
}
void Student::show(){
    cout<<this->name<<"的年龄是"<<this->age<<",成绩是"<<this->score<<endl;

注意,this 是一个指针,要用->来访问成员变量或成员函数。

this 是const指针,它的值不能被修改
this只能在成员函数内部使用,用在其他地方没有意义,也是非法的。
只有当对象被创建之后this才有意义,因此不能在static成员函数中使用。

this到底是什么

this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给this。不过this这个形参是隐式的,它并不出现在代码中

12. static静态成员变量详解

C++中,我们可以使用静态成员变量来实现多个对象共享数据的目标。静态成员变量是一种特殊的成员变量,被关键字static修饰

class Student{
public:
    Student(char *name, int age, float score);
    void show();
public:
    static int m_total;  //静态成员变量
private:
    char *m_name;
    int m_age;
    float m_score;
};

这段代码声明了一个静态成员变量 m_total,用来统计学生的人数。

一个类中可以有多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
static 成员变量和普通static变量一样,都在内存分区的全局数据区分配内存,到程序结束时才释放。
静态成员变量必须初始化,而且只能在类体外进行,例如: int Student::m_total = 10;
静态成员变量既可以通过对象名访问,也可以通过类名访问,但是要遵循private、protected、public。当通过对象名访问时,对于不同的对象,访问的是同一份内存。

13. static静态成员函数详解

普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员(主要作用)。

编译器在编译一个普通成员函数时,会隐式增加一个形参this,并把当前对象的地址赋值给this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。而静态成员函数可以通过类来直接调用,编译器不会为它增加形参this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。

class Student{
public:
    Student(char *name, int age, float score);
    void show();
public:  //声明静态成员函数
    static int getTotal();
    static float getPoints();
private:
    static int m_total;  //总人数
    static float m_points;  //总成绩
private:
    char *m_name;
    int m_age;
    float m_score;
};
//定义静态成员函数
int Student::getTotal(){
    return m_total;
}
float Student::getPoints(){
    return m_points;
}

14. const 成员变量和成员函数(常成员函数

const 成员变量

const 成员变量的用法和普通const变量的用法类似,只需要在声明时加上const关键字,初始化const成员变量唯一的方法:构造函数的初始化列表。

const 成员函数

const 成员函数 也称常成员函数
常成员函数的意义:常成员函数只可以访问const成员变量,而不能修改它,因此getname(),getage()等函数会定义为常成员变量。

常成员函数需要在声明和定义时候在函数头部的结尾加上const关键字:

class Student{
public:
    Student(char *name, int age, float score);
    void show();
    //声明常成员函数
    char *getname() const;
    int getage() const;
    float getscore() const;
private:
    char *m_name;
    int m_age;
    float m_score;
};

Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(){
    cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<endl;
}
//定义常成员函数
char * Student::getname() const{
    return m_name;
}
int Student::getage() const{
    return m_age;
}
float Student::getscore() const{
    return m_score;
}

必须在成员函数的声明和定义处同时加上 const 关键字。

区分const 的位置

  • 函数开头的const用来修饰函数的返回值,表示返回值是const类型,也就是不能被修改,例如:const char* getname()
  • 函数头的结尾加上const表示常成员函数,这种函数只能读取成员变量的值,不能修改成员变量的值,例如 char* getname() const

15. const 对象

const修饰的对象称作常对象。一旦将对象定义为常对象,就只能调用类的const成员(包括const成员变量和const成员函数)了。

定义常对象的语法和定义常量的语法类似

const class object(params);
class const object(params);

当然也可以定义const 指针:

const class *p = new class(params);
class const* p = new class(params);

一旦将对象定义为常对象后,不管是哪种形式,该对象就只能访问const修饰的成员了(包括const成员变量和const成员函数)。

class Student{
public:
	 char *getname() const;
    int getage() const;
    float getscore() const;
private:
	char *m_name;
    int m_age;
    float m_score;
};

char* Student::getname() const{
	return m_name;
}
int Student::getage() const{
	return m_age;
}
float Student::getscore() const{
	return m_score;
}

int main()
{
	const Student stu("小明", 15, 90.6);
	cout<<stu.getname()<<"的年龄是"<<stu.getage()<<",成绩是"<<stu.getscore()<<endl;
    const Student *pstu = new Student("李磊", 16, 80.5);
    //pstu -> show();  //error
    cout<<pstu->getname()<<"的年龄是"<<pstu->getage()<<",成绩是"<<pstu->getscore()<<endl;
	
	return 0;
}

本例中,stu、pstu 分别是常对象以及常对象指针,它们都只能调用 const 成员函数。

16. 友元函数和友元类(C++ friend 关键字)

在C++中,一个类可以有public、protected和private三种属性的成员,通过对象可以访问public成员,只有本类中的函数可以访问本类的private成员。但是,我们可以借助友元(friends),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的private成员

1. 友元函数

在当前类以外定义的、不属于当前类的函数可以在类中声明,但是在前面加friend关键字,这样就构成了友元函数,友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。

友元函数可以访问当前类中的所有成员,包括public、protected、private属性

  • 将非成员函数声明为友元函数:
class Student{
public:
	Student(char* name, int age, float score);
public:
	friend void show(Student *pstu);		// 将show() 声明为友元函数
private:
	char* m_name;
	int m_age;
	float m_score;
};

Student::Student(char* name; int age, float score):m_name(name),m_age(age),m_score(score){ }
// 非成员函数
void show(Student *pstu){
	cout<<pstu->m_name << "的年龄是" << pstu->m_age<<", 成绩是" << pstu->m_score<<endl;
}

int main()
{
	Student stu("小明", 15, 90.6);
	show(&stu);		// 调用友元函数
	Student *pstu = new Student("李磊", 16, 80.5);
	show(pstu);		// 调用友元函数
	
	return 0;
}

友元函数的关键所在:普通的类对象是不可以访问private 和 protected类成员的,但是把类对象通过参数传递给友元函数,友元函数内可以通过类对象访问private和protected类成员,这就是友元函数的key,但是这样也破坏了数据的隐藏,因此要谨慎使用

注意:友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象(这里的对象既可以是对象指针,也可以是普通的对象)。

友元类

不仅可以将一个函数声明为一个类的友元函数,还可以将一个类声明为另一个类的友元类。友元类中的所有成员函数都是另外一个类的友元函数。

例如将类B声明为类A的友元类,那么类B中的所有成员函数都是类A的友元函数,可以访问类A的所有成员,包括public、protected、private属性的。

声明语法:

friend class Student;
#include 
using namespace std;
class Address;  //提前声明Address类
//声明Student类
class Student{
public:
    Student(char *name, int age, float score);
public:
    void show(Address *addr);
private:
    char *m_name;
    int m_age;
    float m_score;
};
//声明Address类
class Address{
public:
    Address(char *province, char *city, char *district);
public:
    //将Student类声明为Address类的友元类
    friend class Student;
private:
    char *m_province;  //省份
    char *m_city;  //城市
    char *m_district;  //区(市区)
};
//实现Student类
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(Address *addr){
    cout<<m_name<<"的年龄是 "<<m_age<<",成绩是 "<<m_score<<endl;
    cout<<"家庭住址:"<<addr->m_province<<"省"<<addr->m_city<<"市"<<addr->m_district<<"区"<<endl;
}
//实现Address类
Address::Address(char *province, char *city, char *district){
    m_province = province;
    m_city = city;
    m_district = district;
}
int main(){
    Student stu("小明", 16, 95.5f);
    Address addr("陕西", "西安", "雁塔");
    stu.show(&addr);
   
    Student *pstu = new Student("李磊", 16, 80.5);
    Address *paddr = new Address("河北", "衡水", "桃城");
    pstu -> show(paddr);
    return 0;
}
  • 友元的关系是单向, 而不是双向的
  • 友元的关系不能传递,如果B是A的友元类,类C是B的友元类,不等于C是A的友元类。

除非有必要,一般不建议把整个类声明为友元类,而只是将某些成员函数声明为友元函数,这样更安全一些。

18. class和struct的区别

C++保留了C语言的struct关键字,并且加以扩充。在C语言中,struct只能包含成员变量,不能包含成员函数。而在C++中,struct类似于class,既可以包含成员变量,又可以包含成员函数。

C++中的struct和class是基本通用的,唯有几个不同细节:

  • 使用class时,类中的成员默认都是private属性的,而使用struct时,结构体中的成员默认都是public属性的。
  • class 继承默认是private继承,而struct继承默认是public继承。
  • class可以使用模板,而struct不能使用。

建议使用class定义类,使用struct来定义结构体

19. String 详解, C++字符串详解

string 是C++中常用的一个类
使用string 类需要包含头文件string,下面例子介绍了几种string变量(对象)的方法:

#include 
#include 
using namespace std;

int main(){
	string s1;
	string s2 = "c plus plus";
	string s3 = s2;
	string s4(5, 's')
	return 0;
}
  • 变量s1 只是定义但没有初始化,编译器会默认赋值给s1,默认是"",即空字符串。
  • 变量s2在定义的同时被初始化为”c plus plus“,与C风格字符串不哦那个,string 的结尾没有结束标志\0
  • 变量s3在定义的时候直接使用s2 进行初始化,因此s3 内容也是c plus plus
  • 变量s4被初始化为由5个s字符组成的字符串,也就是”sssss“。

当我们需要知道字符串长度,可以调用string类提供过的length()函数,如下:

string s = "https://c.bianchegn.net";
int len = s.length(); // 由于string 的结尾没有`\0`字符,所以length()返回的字符串是真实长度,而不是长度+1
cout<<len<<endl;

有必要转换为C风格的字符串

虽然C++中提供了string类来代替C语言中的字符串,但是在实际编程中,有时候必须要使用C风格的字符串(例如打开文件时的路径),因此string类为我们提供了一个转换函数c_str()该函数能够将string字符串转换为C风格的字符串,并返回该字符串的const(const char*):

string path = "D:\\demo.txt";
FILE* fp = fopen(path.c_str(), "rt");

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

string字符串的输入输出

#include 
#include 

using namespace std;
int main(){
	string s = "1234567890";
	for(int i=0, len=s.length(); i<len; i++){
		cout<<s[i]<<" ";
	}
	cout<<endl;
	s[5] = '5';
	cout<<s<<endl;
	return 0;
}

字符串的拼接

有了string 类,我们可以使用++= 运算符来直接拼接字符串。

+来拼接字符串时,运算符的两边可以都是string字符串,也可以是一个string字符串和一个C风格字符串,还可以是一个string字符串和一个字符数组,或者是一个string字符串和一个字符。

#include 
#include 

using namespace std;

int main(){
    string s1 = "first ";
    string s2 = "second ";
    char *s3 = "third ";
    char s4[] = "fourth ";
    char ch = '@';
    string s5 = s1 + s2;
    string s6 = s1 + s3;
    string s7 = s1 + s4;
    string s8 = s1 + ch;
    
    cout<<s5<<endl<<s6<<endl<<s7<<endl<<s8<<endl;
    return 0;
}

string 字符串的增删改查

插入字符串

insert()函数可以在string字符串中指定的位置 插入另一个字符串,它的原型:
string & insert(size_t pos, const string& str);
pos表示要插入的位置,也就是下标;str表示要插入的字符串,它可以是string字符串,也可以是C风格的字符串。

string s1, s2, s3;
s1 = s2 = "1234567890";
s3 = "aaa";
s1.insert(5, s3);
s2.insert(5, "bbb");

删除字符串

erase()可以删除string中的一个子字符串,它的原型为:
string& erase(size_t pos=0, size_t len = npos);
pos表示要删除的字符串的起始下标,len表示要删除字符串的长度。如果不指明len的话,那么要直接删除从pos到字符串结束处的所有字符(此时len = str.length - pos)。

string s1 = s2 = s3 = "1234567890";
s2.erase(5);
s3.erase(5, 3);

在pos参数没有越界的情况下,erase()函数会从一下两个值中取最小的一个作为待删除字符串的长度;

  • len的值;
  • 字符串长度减去pos的值。

提取子字符串

substr()函数用于从string字符串中提取子字符串,它的原型:
string substr(size_t pos = 0, size_t len = npos) const;
pos 为要提取的字符串的起始下标,len为要提取的字符串的长度。

string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6);

系统对substr()参数的处理和erase()类似:

  • 如果pos越界,会抛出异常;
  • 如果len越界,会提取从pos到字符串结尾处的所有字符。

字符串查找

string 提供了几个与字符串 查找相关的函数,如下所示:

  • find() 函数
    find()函数用于在string字符串中查找子字符串出现的位置,它的其中两种原型为:

    size_t find(const string &str, size_t pos=0) const;
    size_t find(const char* s, size_t pos = 0) const;
    if(index < s1.length())
        cout<<"Found at index : "<< index <<endl;
    else
        cout<<"Not found"<<endl;
    

    第一个参数为待查找的子字符串,它可以是string字符串,也可以是C风格的字符串。第二个参数是开始查找的位置(下标);如果不指明,则从0个字符开始查找。

  • rfind() 函数
    rfind()和find()很类似,同样是在字符串中查找字符串,不同的是rfind()函数则最多查找到第二个参数处,如果到了第二个参数所指的下标还没有找到子字符串,则返回string::npos。

    string s1 = "first second third";
    string s2 = "second";
    int index = s1.rfind(s2, 6);
    if(index < s1.length())
        cout<<"Found at index : "<< index <<endl;
    else
        cout<<"Not found"<<endl;
    
  • find_first_of() 函数
    find_first_of()函数用于查找子字符串和字符串共同具有的字符在字符串中首次出现的位置。

    string s1 = "first second second third";
    string s2 = "asecond";
    int index = s1.find_first_of(s2)
    if(index < s1.length())
    	cout<<"Found at index:"<< index <<endl;
    else
    	cout<< "Not Found"<< endl;
    return 0;
    

本例中 s1 和 s2 共同具有的字符是‘s’,该字符在 s1 中首次出现的下标是3,故查找结果返回3。

你可能感兴趣的:(C/C++基础,c++,c语言)