c++学习之类与对象2

简介: c++学习之类与对象2

1.explicit关键字

explicit关键字 修饰构造函数 作用是不能通过隐式法调用构造函数。

首先我们先了解构造函数的隐式转换:

#include<iostream>
using namespace std;
class mystring
{
public:
  mystring(int n)
  {
    cout << "mysting(const char *str)" << endl;
  }
  mystring(const char* str)
  {
    cout << "mysting(conts char *str)"<<endl;
  }
};
int main()
{
  //给字符串赋值?还是初始化?
  mystring st1=1;//隐式构造函数
  //因为实例化对象要调动构造函数,而这里就会寻找构造函数其参数为整形的,而不是赋值
  //所以这是初始化
  mystring str2(10);//构造函数赋值,这里看比较显而易见
  //寓意非常明显,给字符串赋值
  mystring st3 = "abcd";
  mystring str4 = ("abcd");
  return 0;
}

我们发现有时候隐式的构造函数赋值时写法会与赋值这两种混淆含义,不清楚该操作是初始化构造函数还是给对象赋值,于是我们使用了一个关键字explicit,用来修饰构造函数不会有初始化是以上代码“mystring  str2=1”这种方式,这种方式只能是给认为给对象赋值。

explicit mystring(int n)
  {
    cout << "mysting(const char *str)" << endl;
  }

这样修饰构造函数之后,在这样写是无法找到对印的构造函数:

744359616ea84416a6b92d22c7340eca.png

类的对象数组

本质上是一个数组,其成员为类的对象。

举例:

class A
{
public:
  int a;//这里我们为了便于访问就直接定义为公有的
public:
  A()
  {
    a = 0;
    cout << "A的无参构造a=" << a << endl;
  }
  A(int x)
  {
    a = x;
    cout << "A的有参构造a=" << a << endl;
  }
  ~A()
  {
    cout << "A的析构函数 a=" << a << endl;
  }
};
int main()
{
  //对象数组  每个元素都会自动调用构造函数和析构函数
  //对象数组不初始化  每个元素是调用无参构造的
  A arr1[5];
  //对象初始化时,必须显示使用有参构造  逐个元素初始化
  A arr2[5] = { A(10),A(20),A(30),A(40),A(50) };
  int n = sizeof(arr2) / sizeof(arr2[0]);
  int i = 0;
  for (i = 0; i < n; i++)
  {
    cout << arr2[i].a<<" " ;
  }
  cout << endl;
}

在这里,数组元素即类的对象的创建是遵循栈的规则,先创建后释放。

1a79aad1be544b6d919ded181d5d942d.png

我们可以观察构造函数调用与释放时的顺序。创建时先从前往后创建,释放时从后往前释放。

动态对象的创建与初始化

1.动态创建的概述

当我们创建数组的时候,总需要提前预定数组长度,然后编译器分配预定长度的数组空间。但我们学了c语言就知道,在不知道的数组大小前提下,给定空间可能太大,浪费空间,可能太小,空间不够用。而动态创建专门解决这类问题,在c语言中提供专门开辟空间的函数malloc  realloc,calloc结合释放空间的函数free是现在堆区上动态开辟空间,需要多少,开辟多少。

2.c语言方式创建动态对象

当创建一个c++对象时会发生两件事:

1.为对象分配内存

2.调用构造函数初始化那片内存空间。

而第二步时确保一定能发生的。c++强迫我们必须要这样做是因为使用未初始化的对象是程序报错的重要原因。在c语言中提供专门开辟空间的函数malloc  realloc,calloc,这些函数式时能有效开辟空间的,但是原始的,需要我们小心谨慎的使用。

举例:利用c动态开辟的的方式

class person
   {
 public:
   person()
     {
     cout << "person无参构造" << endl;
     }
   ~person()
     {
     cout << "person析构" << endl;
     }
   int a;
     };
 void test01()
 {
   person * p = (person*)malloc(sizeof(person));
//判断是否开辟成功
//调用初始化函数
//清理对象
   free(p);
   }
int main()
 {
   test01();
   return 0;
 }

注意:malloc和free动态申请对象和释放对象 使用malloc和free函数去动态申请对象,和释放申请的对象时不会调用构造函数和析构函数。但是我们同样我们需要自己去调用初始化函数和清理对象的函数。

我们可以看到这是对于c++来说是,反其道而行之,我们必须自己去初始化,并清理。

出现的问题有:

1.程序员必须确定对象长度

2.malloc时返回一个void的指针,在c++中是不允许将void型的赋值给其他指针

3.malloc可能会申请内存失败

4.用户在使用对象前必须亲自初始化,之后亲自清理。

c的动态申请太复杂,我们在c++中不适用。

c++对象的动态申请

1.new创建动态对象

c++中解决动态分配的方案是把创建的一个对象所需的操作都结合在一个称为new的运算符中,当用new创建一个对象时,它就在堆区为对象分配所需的空间并完成初始化。如下:

person *person=new person;

deee00eb93424105a6b518b1f96589cc.png


new操作符能确定再调用构造函数初始化前内存分配时成功的,所以不用显示是否调用成功。因此在队里创建对象的过程变得更简单了。

2.delete释放动态对象

new的对头时delete,delete先调用析构函数,然后释放内存。

88c9e18f8dfb4da8b9800cde1657de4d.png

举例:

class person
{
public:
  person()
  {
    cout<<"无参构造函数!"<<endl;
    pname = new char[strlen("undefined") + 1];
    strcpy(pname, "undefined");
    page = 0;
  }
  person( char *arr,int x)
  {
    cout << "有参构造函数!" << endl;
      pname = new char[strlen(arr) + 1];
      strcpy(pname, arr);
    page = x;
  }
  void showperson()
  {
    cout << "name:" << pname << "age:" << page << endl;
  }
  ~person()
  {
    cout << "调用了析构函数" << endl;
    if (pname != NULL)
    {
      delete[]pname;
      pname = NULL;
    }
  }
  //这里为了方便调用还是用公共型的
public:
  char* pname;
  int page;
};
int main()
{
  person* person1 = new person;//自动调用无参构造
  person* person2 = new person((char *)"john", 33);//自动调用有参构造
  person1->showperson();
  person2 -> showperson();
  delete person1;
  delete person2;
}

动态对象数组

所创建的的对象数组为动态开辟,举例

class person
{
public:
  person()
  {
    pname = NULL;
    page = 0;
  }
  person(char* name, int age)
  {
    pname = new char[strlen(name) + 1];
    strcpy(pname, name);
    page = age;
  }
  ~person()
  {
    if (pname != NULL)
    {
      delete []pname;
      pname = NULL;
    }
  }
public:
  char* pname;
  int page;
};
void test()
{
  //栈聚合初始化
  person sperson[] = { person((char*)"john",12),person((char*)"marke",14) };
  cout << sperson[1].pname << endl;
  //创建堆上的数组对象必须提供构造函数
  person* workers = new person[20];
  delete []workers;
}

可以看到new与delete 是成对出现的,注意他们的使用方法:

类型 *p = new 类型;

delete p; 申请数组:

类型 *p = new 类型[n];

delete [ ]p;

d174d53fe29243e29d6707154c9c6e5e.png


静态成员

在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用 关键字static声明为静态的,称为静态成员。 不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所 有属于这个类的对象共享。

静态成员变量

static修饰的静态成员是属于类的,而不是对象

静态成员变量在内存中只有一份

多个对象共享一个静态变量 静态成员变量

必须类内声明,类外定义 静态成员变量可以通过类的作用域访问

静态成员变量可以通过类的对象访问


static 修饰的成员,定义类的时候,必须分配空间(在编译阶段)。


举例:

class person
{
 public:
   int a;//普通变量
   //静态成员变量不能再类内初始化 类内只能声明,定义在全局 声明的作用只是限制静态
   //成员变量作用域
   static int b;//静态成员变量 在编译阶段就分配内存 存在静态全局区
 };
int person::b = 10;//类中成员变量的定义
 void test01()
{
   person p1;
   p1.b = 100;
   cout << p1.b << endl;
 }
 void test02()
 {
   cout << person::b << endl;//通过类的作用域访问类的静态成员函数,无需再今经过对象
  //cout << person::a << endl;
 }
 using namespace std;
 int main()
 {
    test01();
  test02();
  return 0;
 }

6ebbb572e0eb4b6f9037b74de3575828.png


可以看到静态修饰后,它可以通过外部修改。

静态成员函数

跟静态成员变量一样,是直属于类的,直接通过类访问问。

.静态成员函数能访问静态成员变量不能访问普通的成员变量

.可以通过类的作用域访问静态成员函数

.可以通过对象访问静态成员函数

与静态变量不一样,变量在类之前就已经定义好了(外部定义)。但函数可以直接在内部定义。

举例:

class person
   {
public:
   int a;
   //静态成员变量不能再类内初始化 类内只能声明,定义在全局 声明的作用只是限制静态
   //成员变量作用域
     static int b;//静态成员变量 在编译阶段就分配内存 存在静态全局区
   void show()
     {
     cout << a << " " << b << endl;;
     }
   static void static_show()//静态成员函数 可以访问静态成员变量 不能访问普通的
    //成员变量
     {
     cout << " " << b << endl;;
     }
   };
int person::b = 100;
 void test01()
 {
   person::static_show();//通过流类的作用域访问静态成员函数
   person p1;
   p1.static_show();//通过对象访问静态成员函数
}
 int main()
 {
   test01();
   return 0;
 }

值得注意的是,静态成员函数只能操作静态成员变量,对普通的变量的无法操作。

相关文章
|
7月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
7月前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
6月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
6月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
6月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!
|
8月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
170 19
|
8月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
135 16
|
9月前
|
算法 网络安全 区块链
2023/11/10学习记录-C/C++对称分组加密DES
本文介绍了对称分组加密的常见算法(如DES、3DES、AES和国密SM4)及其应用场景,包括文件和视频加密、比特币私钥加密、消息和配置项加密及SSL通信加密。文章还详细展示了如何使用异或实现一个简易的对称加密算法,并通过示例代码演示了DES算法在ECB和CBC模式下的加密和解密过程,以及如何封装DES实现CBC和ECB的PKCS7Padding分块填充。
192 4
2023/11/10学习记录-C/C++对称分组加密DES
|
8月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
206 13
|
7月前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。