C++基础知识(四:类的学习)

简介: 类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。


目录

【1】定义

【2】类和结构体的区别

【3】this指针**

【4】类中的特殊成员函数

【5】构造函数

ii)构造函数提供了初始化列表的机制

iii)需要使用初始化列表的情况

【6】析构函数

i)格式

ii)调用时机

iii)需要显性写出析构函数的场景

【7】拷贝构造函数

i)格式

ii)使用

iii)深浅拷贝问题**

iv)拷贝构造函数的调用时机

【8】拷贝赋值函数

i)格式

ii)代码

【9】匿名对象

【10】C++中结构体和C的区别以及C++中结构体和类的区别


类指的就是对同一类对象,把所有的属性都封装起来,你也可以把类看成一个高级版的结构体。

【1】定义

class 类名

{

访问权限:

   成员属性;

访问权限:

   成员方法;

}


访问权限:

public:共有的,类内、类外和子类中都可以访问

private:私有的,类内可以访问,类外和子类中都不能访问,类中默认是私有权限

protected:受保护的,类内和子类中可以访问,类外不可以访问(继承再说)


访问权限,是针对于一个类来说的

【2】类和结构体的区别

  1. 类的封装性比结构体更好,类中默认是私有权限,结构体中默认是共有权限
  2. 结构体默认是公有继承,类默认是私有继承
  3. C++中类就是由结构体演变来的
  4. 结构体一般用于实现某种数据结构时,类一般用于描述一类对象的性质

【3】this指针**

每一个类中的非静态成员函数,都有一个this指针,指向调用者,(非静态成员数隐藏 的形参)

谁调用this就指向谁哪一个类对象调用成员函数,就会用该类对象的首地址,初始化形参this

原型:类类型  *const this; ----->指针的指向不能修改

需要使用this指针的场合

  1. 当形参和成员属性同名
  2. 拷贝赋值函数,需要返回自身的引用

#include <iostream>


using namespace std;

class Rec

{

private:

   int len;

   int wid;

public:

   void set(int l,int w);  //设置长和宽

   void show();   //输出面积和周长

   int get_l();     //获取长和宽

   int get_wid();

};


void Rec::set(int len, int wid)   //r1.set()

{

   this->len = len;

   this->wid = wid;

}


void Rec::show()

{

   cout << "周长:" << (len+wid)*2 << endl;

   cout << "面积:" << len*wid << endl;

}


int Rec::get_l()

{

   return len;

}


int Rec::get_wid()

{

   return wid;

}

int main()

{

   Rec r1;  //r1是一个类对象,实例化了一个类对象r1

   r1.set(9,2);

   Rec r2;

   r2.set(9,2);

   cout << "&r1=" << &r1 << endl;

   return 0;

}

【4】类中的特殊成员函数

特殊的构造函数都是public权限

类中,会默认提供一些特殊的成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数

【5】构造函数

构造函数支持函数重载

构造函数,会在实例化类对象时,系统默认调用无参构造;

如果用户手动定义出了构造函数,系统将不再提供构造函数

类名()

{

  //函数体

}

调用时机

栈区:实例化类对象时,自 image.gif 编辑动调用

堆区:什么时候使用new申请空间,什么时候调用构造函数

image.gif 编辑

当提供构造函数后

image.gif 编辑

ii)构造函数提供了初始化列表的机制

如果在函数体内部,给成员属性赋值,是一个赋值的过程,不是初始化的过程

类名():成员属性1(形参的值1),成员属性2(形参的值)`````


在函数头后面,使用:引出初始化列表,每个成员属性以,分隔,()里面是形参,外面是成员属性

iii)需要使用初始化列表的情况

  1. 形参和成员属性同名
  2. 类中有引用成员时,必须使用初始化列表

#include <iostream>


using namespace std;

class Stu

{

   int age;

   float high;

   int &score;

public:

   //构造函数支持函数重载

   Stu(int age,float high,int a):age(age),high(high),score(a)

   {

       cout << "Stu的有参构造" << endl;

   }

};


int main()

{

   //Stu s;

   int n1 = 90;

   Stu s1(18,9,n1);    //在栈区

   Stu *p;

   //p = new Stu;   //在堆区申请了Stu类对象的空间


   return 0;

}

  1. 类中有const修饰的成员时,必须使用初始化列表

#include <iostream>


using namespace std;

class Stu

{

   int age;

   float high;

   const int score;

public:

//    Stu()

//    {

//        cout << "Stu的无参构造函数" << endl;

//    }

   //构造函数支持函数重载

   Stu(int age,float high,int a):age(age),high(high),score(a)

   {

       cout << "Stu的有参构造" << endl;

   }

};


int main()

{

   //Stu s;

   int n1 = 90;

   Stu s1(18,9,89);    //在栈区

   Stu *p;

   //p = new Stu;   //在堆区申请了Stu类对象的空间


   return 0;

}

  1. 类中含有其他类的子对象时,必须使用初始化列表(类的包含关系)

(如果另一个类只有有参构造需要在初始化列表中宏显性调用,如果另一个类有无参构造,可以不写初始化列表)

image.gif 编辑

               类的包含关系

#include <iostream>


using namespace std;

class Per

{

   string name;

public:

   Per(string name)

   {

       this->name = name;

       cout << "Per的有参构造" << endl;

   }

};


class Stu

{

   int age;

   float high;

   int score;

   Per p1;   //Per类只有有参构造函数

public:

   Stu():p1("zhangsan")

   {

       cout << "Stu的无参构造函数" << endl;

   }

   //构造函数支持函数重载

   Stu(int age,float high,int a,string name):p1(name)

   {

       this->age = age;

       this->high = high;

       this->score = a;

       cout << "Stu的有参构造" << endl;

   }

};


int main()

{

   Stu s;

   int n1 = 90;

   Stu s1(18,9,89,"lisi");    //在栈区


   return 0;

}

【6】析构函数

不支持函数重载

在类对象空间消亡时,系统自动调用

i)格式

~类名()

{

    //函数体

}

ii)调用时机

栈区:对象消亡时,自动调用

堆区:什么时候delete,什么时候调用

构造函数和析构函数调用的时机:

先构造的后析构,后构造的先析构

iii)需要显性写出析构函数的场景

类中有指针成员,并且指针成员,指向堆区的空间

#include <iostream>


using namespace std;

class Stu

{

   string name;

   int *p;

public:

   Stu():p(new int)   //保证指针成员,指向堆区的空间

   {

       cout << "堆区申请的空间为:" << p << endl;

       cout << "Stu的无参构造" << endl;

   }

//    Stu(string name,int p):name(name),p(new int(p))

//    {

//        cout << "Stu的有参构造" << endl;

//    }

   ~Stu()

   {

       cout << "准备释放堆区的空间:" << p << endl;

       delete p;

       cout << "Stu的析构函数" << endl;

   }


};


int main()

{

   Stu s1;

   return 0;

}

image.gif 编辑

【7】拷贝构造函数

利用一个类对象,给另一个类对象初始化时,自动调用拷贝构造函数

如果自己实现了拷贝构造,系统不再提供默认的拷贝构造

i)格式

类名(同类对象的引用)

{

  //函数体

}

ii)使用

#include <iostream>


using namespace std;

class Per

{

   string name;

public:


   //Per中,自己定义了有参构造,系统不再提供无参构造

   Per(string name)

   {

       this->name = name;

       cout << "Per的有参构造" << endl;

   }

   ~Per()

   {

       cout << "Per的析构函数" << endl;

   }

};


class Stu

{

   int age;

   float high;

   int score;

   //Per p1;   //Per类只有有参构造函数

public:

   Stu()

   {

       cout << "Stu的无参构造函数" << endl;

   }

   //构造函数支持函数重载

   Stu(int age,float high)

   {

       this->age = age;

       this->high = high;

       cout << "Stu的有参构造" << endl;

   }


   //拷贝构造

   Stu(Stu &other)

   {

       //this->age = other.age;

       this->high = other.high;

       cout << "拷贝构造函数" << endl;

   }

   ~Stu()

   {

       cout << "Stu的析构函数" << endl;

   }

   void show();

};

void Stu::show()

{

   cout << "age = " << age << endl;

   cout << "high = " << high << endl;

}

int main()

{

   Stu s1(19,100);

   cout << "s1的show:" << endl;

   s1.show();

   Stu s2=s1;

   cout << "s2的show:" << endl;

   s2.show();

   return 0;

}

iii)深浅拷贝问题**

当类中有指针成员,会涉及到深浅拷贝问题

浅拷贝:两个不同类对象的指针成员,指向同一片空间

问题:析构时,会发生二次释放问题;同一片空间被两个不同的类对象占用,发生资源抢占

深拷贝:两个不同类对象的指针成员,指向不同的空间,但是保存的是同样的数据

image.gif 编辑

浅拷贝

image.gif 编辑

深拷贝

#include <iostream>


using namespace std;

class Stu

{

   string name;

   int *p;

public:

   Stu():p(new int)   //保证指针成员,指向堆区的空间

   {

       cout << "堆区申请的空间为:" << p << endl;

       cout << "Stu的无参构造" << endl;

   }

   Stu(string name,int p):name(name),p(new int(p))

   {

   }

   ~Stu()

   {

       cout << "准备释放堆区的空间:" << p << endl;

       delete p;


       cout << "Stu的析构函数" << endl;

   }


   //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化

   Stu(Stu &other):name(other.name),p(new int(*(other.p)))

   {

       /*this->name = other.name;

       this->p = new int(*(other.p));*/

       cout << "Stu的拷贝构造" << endl;

   }

   void show();

};

void Stu::show()

{

   cout << "name= " << name << endl;

   cout << "p= " << p << endl;

   cout << "*p= " << *p << endl;

}

int main()

{

   Stu s1("zhangsan",18);

   cout << "s1的show:" << endl;

   s1.show();

   Stu s2 = s1;

   cout << "s2的show:" << endl;

   s2.show();

   return 0;

}

iv)拷贝构造函数的调用时机

  1. 使用已有的类对象,给新的类对象初始化
  2. 函数的参数是一个类对象时,也会调用拷贝构造函数
  3. 函数的返回值是一个类对象时,也会调用拷贝构造函数

image.gif 编辑

测试代码:

#include <iostream>


using namespace std;

class Stu

{

   string name;

public:

   Stu()

   {

       cout << "Stu的无参构造" << endl;

   }

   Stu(Stu &other):name(other.name)

   {

       cout << "Stu的拷贝构造函数" << endl;

   }

   Stu(string name):name(name)

   {

       cout << "Stu的右参构造" << endl;

   }

};


Stu fun(Stu s1)

{

   return s1;

}

int main()

{

   Stu s;

   //Stu s2(fun(s));   //会报错,因为fun(s)的返回值是一个临时值,不能引用

   return 0;

}

【8】拷贝赋值函数

使用已有的类对象,给另外一个已有的类对象赋值

系统默认提供一个拷贝赋值函数

本质:赋值运算符的重载

i)格式

类名 &operator=(const 类名&other)

{

 //函数体

}

ii)代码

#include <iostream>


using namespace std;

class Stu

{

   string name;

   int *p;

public:


   //深拷贝赋值函数

   Stu &operator=(const Stu &other)

   {

       name = other.name;

       *p = *(other.p);

       cout << "Stu的拷贝赋值函数" << endl;

       return *this;

   }

   Stu():p(new int)   //保证指针成员,指向堆区的空间

   {

       cout << "堆区申请的空间为:" << p << endl;

       cout << "Stu的无参构造" << endl;

   }

   Stu(string name,int p):name(name),p(new int(p))

   {

   }

   ~Stu()

   {

       cout << "准备释放堆区的空间:" << p << endl;

       delete p;


       cout << "Stu的析构函数" << endl;

   }


   //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化

   Stu(Stu &other):name(other.name),p(new int(*(other.p)))

   {

       /*this->name = other.name;

       this->p = new int(*(other.p));*/

       cout << "Stu的拷贝构造" << endl;

   }

   void show();

};

void Stu::show()

{

   cout << "name= " << name << endl;

   cout << "p= " << p << endl;

   cout << "*p= " << *p << endl;

}

int main()

{

   Stu s1("zhangsan",18);

   cout << "s1的show:" << endl;

   s1.show();

   Stu s2;

   s2 = s1;

   cout << "s2的show:" << endl;

   s2.show();

   return 0;

}


【9】匿名对象

没有对象名,通过类名实例化出来的对象,类名();

Stu();生命周期更短

  1. 全局函数传参
  2. 类数组赋值     //int a=9,b=7,c=8;    int arr[3]={a,b,c};   //int arr[3]={9,7,8};
  3. 临时调用类中的成员函数
  4. 给新的类对象赋值

#include <iostream>


using namespace std;

class Stu

{

   string name;

   int *p;

public:


   //深拷贝赋值函数

   Stu &operator=(const Stu &other)

   {

       name = other.name;

       *p = *(other.p);

       cout << "Stu的拷贝赋值函数" << endl;

       return *this;

   }

   Stu():p(new int)   //保证指针成员,指向堆区的空间

   {

       cout << "堆区申请的空间为:" << p << endl;

       cout << "Stu的无参构造" << endl;

   }

   Stu(string name,int p):name(name),p(new int(p))

   {

   }

   ~Stu()

   {

       cout << "准备释放堆区的空间:" << p << endl;

       delete p;


       cout << "Stu的析构函数" << endl;

   }


   //使用同类其他对象的指针成员解引用后的值,给自己的指针成员的内容初始化

   Stu(const Stu &other):name(other.name),p(new int(*(other.p)))

   {

       /*this->name = other.name;

       this->p = new int(*(other.p));*/

       cout << "Stu的拷贝构造" << endl;

   }

   void show();

};

void Stu::show()

{

   cout << "name= " << name << endl;

   cout << "p= " << p << endl;

   cout << "*p= " << *p << endl;

}


void fun(Stu s1)

{

   cout << "调用成功" << endl;

}

int main()

{

   //1、使用匿名对象用做全局函数传参

   fun(Stu());   //匿名对象的生命周期,只在定义语句的位置,是一个临时值

   

   //2、想要临时使用类中的成员函数

   Stu().show();


   //3、给类对象的数组赋值

   Stu arr[3]={Stu("zhangsan",8),Stu("lisi",19),Stu("xiaoming",20)};


   //4、给新的类对象赋值

   Stu s3(Stu("zhangsan",18));

   return 0;

}

【10】C++中结构体和C的区别以及C++中结构体和类的区别

  1. C中定义需要加struct,C++中可以不加struct
  2. C++中结构体可以有访问权限的控制(public、private、protected)
  3. C++中结构体可以继承
  4. C++中结构体可以封装函数
  5. C++中结构体内可以定义另外一个结构体声明(类型)

结构体和类的区别:

  1. 使用场合不同,类适用于某一类对象属性和方法的封装,结构体用于某种数据结构的实现
  2. 类中默认private,结构体中默认是public
  3. 类默认是私有继承,结构体默认是共有继承
  4. 类的封装性比结构体的封装性更好
相关文章
|
2天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
2天前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
2天前
|
C语言 C++ 开发者
C++基础知识(一:命名空间的各种使用方法)
C++在C的基础上引入了更多的元素,例如类,类的私密性要比C中的结构体更加优秀,引用,重载,命名空间,以及STL库,模板编程和更多的函数,在面向对象的编程上更加高效。C语言的优势则是更加底层,编译速度会更快,在编写内核时大多数都是C语言去写。 在C++中,命名空间(Namespace)是一种组织代码的方式,主要用于解决全局变量、函数或类的命名冲突问题。命名空间提供了一种封装机制,允许开发者将相关的类、函数、变量等放在一个逻辑上封闭的区域中,这样相同的名字在不同的命名空间中可以共存,而不会相互干扰。
|
2天前
|
C++
【C++】string类的使用④(常量成员Member constants)
C++ `std::string` 的 `find_first_of`, `find_last_of`, `find_first_not_of`, `find_last_not_of` 函数分别用于从不同方向查找目标字符或子串。它们都返回匹配位置,未找到则返回 `npos`。`substr` 用于提取子字符串,`compare` 则提供更灵活的字符串比较。`npos` 是一个表示最大值的常量,用于标记未找到匹配的情况。示例代码展示了这些函数的实际应用,如替换元音、分割路径、查找非字母字符等。
|
2天前
|
C++
C++】string类的使用③(修改器Modifiers)
这篇博客探讨了C++ STL中`string`类的修改器和非成员函数重载。文章介绍了`operator+=`用于在字符串末尾追加内容,并展示了不同重载形式。`append`函数提供了更多追加选项,包括子串、字符数组、单个字符等。`push_back`和`pop_back`分别用于在末尾添加和移除一个字符。`assign`用于替换字符串内容,而`insert`允许在任意位置插入字符串或字符。最后,`erase`函数用于删除字符串中的部分内容。每个函数都配以代码示例和说明。
|
2天前
|
安全 编译器 C++
【C++】string类的使用②(元素获取Element access)
```markdown 探索C++ `string`方法:`clear()`保持容量不变使字符串变空;`empty()`检查长度是否为0;C++11的`shrink_to_fit()`尝试减少容量。`operator[]`和`at()`安全访问元素,越界时`at()`抛异常。`back()`和`front()`分别访问首尾元素。了解这些,轻松操作字符串!💡 ```
|
2天前
|
存储 编译器 Linux
【C++】string类的使用②(容量接口Capacity )
这篇博客探讨了C++ STL中string的容量接口和元素访问方法。`size()`和`length()`函数等价,返回字符串的长度;`capacity()`提供已分配的字节数,可能大于长度;`max_size()`给出理论最大长度;`reserve()`预分配空间,不改变内容;`resize()`改变字符串长度,可指定填充字符。这些接口用于优化内存管理和适应字符串操作需求。
|
2天前
|
C++ 容器
【C++】string类的使用①(迭代器接口begin,end,rbegin和rend)
迭代器接口是获取容器元素指针的成员函数。`begin()`返回首元素的正向迭代器,`end()`返回末元素之后的位置。`rbegin()`和`rend()`提供反向迭代器,分别指向尾元素和首元素之前。C++11增加了const版本以供只读访问。示例代码展示了如何使用这些迭代器遍历字符串。
|
2天前
|
存储 编译器 C语言
【C++】string类的使用①(默认成员函数
本文介绍了C++ STL中的`string`类,它是用于方便地操作和管理字符串的类,替代了C语言中不便的字符数组操作。`string`基于`basic_string`模板,提供类似容器的接口,但针对字符串特性进行了优化。学习资源推荐[cplusplus.com](https://cplusplus.com/)。`string`类提供了多种构造函数,如无参构造、拷贝构造、字符填充构造等,以及析构函数和赋值运算符重载。示例代码展示了不同构造函数和赋值运算符的用法。
|
2天前
|
编译器 C++
【C++】类和对象⑤(static成员 | 友元 | 内部类 | 匿名对象)
📚 C++ 知识点概览:探索类的`static`成员、友元及应用🔍。

热门文章

最新文章