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;
 }

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

相关文章
|
2月前
|
编译器 C++
C++之类与对象(完结撒花篇)(上)
C++之类与对象(完结撒花篇)(上)
36 0
|
19天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
49 4
|
20天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
46 4
|
2月前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
2月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
28 4
|
2月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
25 4
|
2月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
2月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
2月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
54 1
|
2月前
|
存储 编译器 C语言
【C++打怪之路Lv3】-- 类和对象(上)
【C++打怪之路Lv3】-- 类和对象(上)
17 0