c类中如果没有使用private关键,c类中小学教师

  c类中如果没有使用private关键,c类中小学教师

  Yyds干货库存

  @toc

  我们已经初步实现了类的内容,但都是基础的。今天我们就来分享一些细节,整理一下我的知识,比较难,所以这个博客我可能写的不是很好。如果你有任何问题,请在评论区留言。我看到了会马上回复。如果我什么都不知道,我会帮你查资料。请理解。

  该类的六个默认成员函数

  这个博客主要是和大家分享成员函数(方法)。里面有很多内容。让我一个一个来。让我们看一些例子作为今天的开始。

  请问这门课有什么内容?

  阶级人士

  };

  这不就是一堂空课吗?里面什么都没有。如果你这么认为,那就有点简单了。是的,我们看到里面什么都没有,但实际上编译器会自动生成六个默认的成员函数,存在于这个类中。至于怎么验证,不用担心单机。

  我们先来看看这六个成员函数,不过我们只详细讲其中的四个,另外两个不是很重要。就简单说一下吧。

  构造函数完成对象的初始化。它不是构造函数。

  析构函数完成资源清理。

  必须调用复制函数来构造对象副本。

  赋值重载重定义运算符

  构造函数,也称为构造函数,帮助我们在c中实例化对象,记住,它们帮助我实例化对象。我们先来看看这个。

  阶级人士

  公共:

  作废打印()

  Cout 我的名字是 _name ,我今年 _age 岁 endl

  空集合(const char* name,const int age)

  strcpy(_name,name);

  _age=年龄;

  私人:

  int _ age

  char _ name[20];

  int main()

  Person per1

  1.per1。集(《张三》,18);//每次都设置

  per1。print();

  返回0;

  }

  每次我们实例化一个对象,我们必须设置它。这是不是有点太麻烦了?有时我们可能会忘记初始化它。于是聪明的程序员就想,是不是可以让编译器在对象实例化的时候帮我们自动初始化,这样就不用说忘记了。于是一个特殊的函数——构造函数出现了。

  构造函数的特征

  现在我们可以正式知道构造函数了,它有以下特征。

  没有返回值。记住返回值是no,不是void。

  函数名与类名相同

  当对象被实例化时,编译器自动调用相应的构造函数

  让我们优化前面的代码,写一个构造函数。

  阶级人士

  公共:

  作废打印()

  Cout 我的名字是 _name ,我今年 _age 岁 endl

  //带两个参数的构造函数(编译器的这个不算)

  人员(常数字符*姓名,常数年龄)

  strcpy(_name,name);

  _age=年龄;

  私人:

  int _ age

  char _ name[20];

  int main()

  Person per1(《张三》,18);

  per1。print();

  返回0;

  }

  默认构造函数

  这是一种特殊的构造函数。需要先看下面,只是初步了解,以后再说。

  编译器自动生成

  无参数构造函数

  所有默认构造函数

  编译器自动生成

  总的来说,构造函数就够了,但对我们来说远远不够。我们需要明白一些事情。在初始代码中,我想问一下类中是否有构造函数。这个问题我一开始就回答了。现有的编译器会自动生成构造函数。

  我们来验证一下。如果我们在类中写自己的构造函数,会发生什么?

  A级

  公共:

  A(int a)

  int main()

  A _ a

  返回0;

  }

  我们开始疑惑,为什么没有写构造函数就报错了。我们一眼就能看出,我们在实例化对象时没有传递参数,这就是问题所在。从这里可以看出,编译器在实例化对象时会自动调用匹配的构造函数,也就是说,实例化对象时构造函数肯定会参与。

  但是看看下面的代码,为什么不报错?原因是编译器自动生成无参数构造函数。至于它的结构,没必要知道。

  B类

  int main()

  B b

  返回0;

  }

  由此可以得出一个结论。如果我们不写构造函数,编译器会自动生成一个,但是如果我们写了,编译器不会。至于什么时候写,什么时候不写,先放在这里吧。在这里拓展还是有点困难。让我们把它放在最后。

  无参数构造函数

  我们自己的无参数构造函数也是默认构造函数。就说到这里吧。

  比较简单。至于代码里的问题,别急,我最后再说。

  A级

  公共:

  a=0;

  作废打印()

  Cout“无参数构造函数”endl

  cout a= a endl

  公共:

  int a;

  int main()

  A a//为什么不写一个A()?

  a.print();

  返回0;

  }

  所有默认构造函数

  这是最后一个。总的来说,我们对这里的默认构造函数已经有了很好的理解,这个更容易理解。让我们来看看现象。

  A级

  公共:

  //所有默认值

  a(整数a=10)

  _ a=a

  作废打印()

  Cout“无参数构造函数”endl

  cout a= _ a endl

  公共:

  int _ a;

  int main()

  A a//为什么不写一个A()?

  a.print();

  返回0;

  }

  为什么,这是戒酒协会

  这就要解决上面遗留的问题了。说实话,这是语法规定的,我很难理解,但是我们可以通过现象来理解一些东西。

  先解编译器生成的。这里解决不了,也不要看这里的现象。记住就好。

  然后开始解自己写的无参数构造函数,我们来看现象。

  A级

  公共:

  作废打印()

  Cout你好,世界’endl;

  int main()

  a a();

  a.print();

  返回0;

  }

  也就是说编译器找不到匹配的构造函数,或者不会报错。

  最后一个是完整的默认构造函数。

  A级

  公共:

  //所有默认值

  a(整数a=10)

  _ a=a

  作废打印()

  Cout“无参数构造函数”endl

  cout a= _ a endl

  公共:

  int _ a;

  int main()

  a a();

  a.print();

  返回0;

  }

  这个错误和上面一样,只是我们不理解这个。默认不允许我们这么做吗?可以,但是这里不允许。

  你可能看我废话了半天。是的,我解释不清楚,但我可以知道一个这样的问题。

  如果一个类中有一个不带参数的构造函数,同时也有一个完全默认的构造函数,我们用A A();实例化一个对象,我想问一下编译器会用哪个构造函数,但是编译器不知道,干脆放弃这个用法,就结束了。

  默认构造函数的优先级

  我想和你说一下这个,但是我不知道我的题名准确不准确,也不知道这个知识点的名称是什么。这里,我先给你描述一下。

  如果一个类中有一个无参数的构造函数,同时也有一个完全默认的构造函数,我们用一个A;调用哪个构造函数?在这里用现象来得到答案。

  A级

  公共:

  //所有默认值

  a(整数a=10)

  Cout all default endl

  //没有参数

  Cout no reference endl

  int main()

  A a

  返回0;

  }

  不好意思,这个会有误差,所以谈不上优先级。大家要记住这一点。无参数构造函数和全默认构造函数不能同时存在。我建议写全默认构造函数。

  默认构造函数的功能

  正如我们前面提到的,构造函数是为我们初始化的。但是这个初始化也有一个很大的问题。这里C之前有一个很大的缺陷,直到C 11才弥补了一部分。

  不要更改内置类型。

  C的这个特点很让人苦恼。当使用默认构造时,内置类型不会被处理,但这有点不可接受。我们一眼就能看出来。

  A级

  公共:

  私人:

  int _ a;

  double _ d;

  int main()

  A a

  返回0;

  }

  C 11矿坑填充

  这个坑太大了。反正我对这个问题破口大骂。还好C 11填了这个坑,但是这个方法也会给新手造成很大的问题。先说方法。

  A级

  公共:

  私人:

  int _ a=0;//在此声明

  double _ d=0.0

  给这个初学者一个误解,int _ a=0;是的,但是打开空间是在实例化一个对象的时候,这只是一个声明,类似于默认函数。

  初始化自定义类型。

  上面的标题也不准确。可以说对于自定义类型,编译器会调用自定义类的==默认构造函数==来帮助初始化。

  A级

  公共:

  Cout 协助初始化 endl

  私人:

  int _ a;

  double _ d;

  B类

  公共:

  A _ aa

  int main()

  B b

  返回0;

  }

  我们需要查看内部的结果,并再次调试。

  有些人可能会看到_aa的内容也没有初始化。这是因为我们没有在类A中写默认初始化,我来重写类A,调试一下。仅此而已。

  A级

  公共:

  //写到这里。

  _ a=0;

  _ d=0.0

  私人:

  int _ a;

  double _ d;

  };

  从这里可以看出,我们需要初始化类中的内置类型,而不是自定义类型。

  说到这里,我们现在需要做一个总结。我们已经学习了构造函数,知道了默认的构造函数,了解了构造函数的作用。这些都很难。

  如果说构造函数是为了初始化,那么析构函数就是为了资源清理,这对于一些比较忘记的程序员来说是个福音。比如我们用malloc开辟一个空间,有时候很容易忘记free,会造成内存泄露。这样会造成一些问题。但是析构函数可以在对象的生命周期结束后自动调用这个析构函数。我们只需要在这个析构函数中自由。

  析构函数的特征

  我们先来看看析构函数的特点,这是我们的基础。

  析构函数名称前面有字符~。

  没有参数,没有返回值。

  一个类有且只有一个析构函数。如果没有明确定义,系统将自动生成默认的析构函数。

  在对象生命周期的末尾,C编译器系统自动调用析构函数。

  编译器自动调用

  我们先来看一个例子。

  A级

  公共:

  a(整数上限=4)

  int * arr=(int *)malloc(sizeof(int)* cap);

  断言(arr);

  a=arr

  memset(arr,0,sizeof(int)* cap);

  _ cap=cap

  ~A()

  _ cap=0;

  免费(a);

  a=nullptr

  私人:

  int * a;

  int _ cap

  int main()

  A a

  返回0;

  }

  从上图我们可以知道,对象A的声明期结束后,编译器会自动调用析构函数来清理资源。

  生成了默认析构函数

  我们需要看看默认生成的析构函数会发生什么,这可以帮助我们更高效地编写代码。

  析构函数会清理内置类型的资源吗?

  抱歉,它不能帮助我们清理内置类型。

  A级

  公共:

  a(整数上限=4)

  int * arr=(int *)malloc(sizeof(int)* cap);

  断言(arr);

  a=arr

  memset(arr,0,sizeof(int)* cap);

  _ cap=cap

  私人:

  int * a;

  int _ cap

  };

  析构函数可以清理自定义类型吗?

  这是可能的,但是你需要调用自定义类型的析构函数,这和默认构造函数的初始化是一样的。

  将调用自定义类型的析构函数。

  A级

  公共:

  ~A()

  自定义类型“endl”的“Cout”析构函数;

  B类

  私人:

  A _ aa

  int main()

  B b

  返回0;

  }

  我们还可以看看如何调用析构函数。

  A级

  公共:

  a(整数上限=4)

  int * arr=(int *)malloc(sizeof(int)* cap);

  断言(arr);

  a=arr

  memset(arr,0,sizeof(int)* cap);

  _ cap=cap

  ~A()

  _ cap=0;

  免费(a);

  a=nullptr

  私人:

  int * a;

  int _ cap

  B类

  公共:

  私人:

  A _ aa

  int main()

  B b

  返回0;

  }

  这样,我们也可以得到一个结果。我们不需要为自定义类型编写析构函数,但是需要为内置类型清理资源,避免内存泄漏。

  创建对象时,能否创建与对象相同的新对象?复制构造是构造函数的一种,也是我们以后上课写作的重点内容。我们需要了解它。

  构造函数:只有一个参数,是对这个类类型对象的引用(通常用const修饰),使用已有的类类型对象。

  创建新对象时由编译器自动调用。

  一开始我们在学习函数的时候,很多时候会给函数输入参数,也就是编译器会开辟另一个空间,把要输入输出的内容的副本放到这个空间里。这是一个简单的值副本。

  我们真的需要好好看看这个价值副本,我们发现它们的地址不一样。

  void func(int b)

  cout a b endl

  int main()

  int a=10

  func(a);

  cout a a endl

  返回0;

  }

  对于一些简单的类型,这个副本就可以了,但现在我想给你看这个。

  void函数(int* pb)

  免费(Pb);

  int main()

  int * arr=nullptr

  arr=(int *)malloc(sizeof(int)* 4);

  func(arr);

  免费(arr);

  返回0;

  }

  我们会发现一个问题。对于某些类型,简单的值复制根本不够。为什么会报告上述错误?原因是我们把数组名作为参数,编译器简单的把它作为指针复制到pb,但是pb指向的内容不变,所以我们把它免费放了两次,程序就会中断。

  复制结构的特征

  我们认识到了值copy,现在可以说copy构造了,也是编译器默认生成的构造函数。函数名与类名相同。

  复制构造函数是构造函数的重载形式。

  复制构造函数的参数只有一个,而且必须通过引用传递,这样会导致无限的递归调用。

  先不说第二个话题,最后分享一下。

  生成默认拷贝构造

  让我们首先来看看默认的复制构造是如何工作的,看看什么时候我们需要编写自己的复制构造。

  A级

  公共:

  A(int a=0,double d=0.0)

  _ a=a

  _ d=d

  ~A()

  _ a=0;

  _ d=0.0

  公共:

  int _ a;

  double _ d;

  int main()

  A _aa(1,3.0);

  cout aa。_a= _aa。_ a;

  cout aa。_d= _aa。_ d endl

  a _ bb(_ aa);

  cout _bb。_a= _bb。_ a;

  cout _bb。_d= _bb。_ d endl

  返回0;

  }

  这个构造可以说是一样的,所以我们不用担心编译器这次不会忽略内置类型。这很好,但也有问题。下面来看看。

  默认构造函数是值副本吗?

  这个问题很严重。我们应该知道我们的对象会在生命周期结束后调用析构函数。如果这种情况发生两次,我想大家都会诅咒这位母亲。

  让我们拭目以待

  A级

  公共:

  a(整数上限=4)

  int * pa=(int *)malloc(sizeof(int)* cap);

  断言(pa);

  _ array=pa

  ~A()

  free(_ array);

  _ array=nullptr

  公共:

  int * _ array

  无效函数(A _bb)

  cout _bb。_ array endl

  int main()

  A _ aa

  func(_ aa);

  cout _aa。_ array endl

  返回0;

  }

  这意味着默认情况下只生成一个简单的值副本,意味着后面的代码会被中断,同一个空间会被多次释放。

  int main()

  A _ aa

  a _ bb(_ aa);

  返回0;

  }

  手写构造函数

  分享了这么多,我们好像还没有一个手写的构造函数。这里有一个普通的,但是有很多细节在里面。

  A级

  公共:

  A(int a=0,double d=0.0)

  _ a=a

  _ d=d

  A(常数A)

  _ a=a. _ a

  _ d=a. _ d

  私人:

  int _ a;

  double _ d;

  };

  我们开始挖掘细节。

  为什么要用const装修?

  很好,我们不需要const装饰,但有时我们会编写这样的代码。

  A(常数A)

  a._ a=_ a//倒着写

  _ d=a. _ d

  }

  修改const可以避免这个错误,因为它不编译,但是可以很快发现问题。

  为什么要用参考文献?

  你找到了最重要的东西。首先你要记住,要复制字体color=red自定义类型,必须先调用构造函数/font。如果你写一个公共参数,你需要复制它。编译器开始寻找构造函数,找到构造函数发现需要复制。有一个死循环,所以我们必须使用别名来避免复制。

  内置类型的构造函数呢?

  这个我们已经详细讲过了,这里有个结论。如果你的类中没有这种类似的指向同一个空格的属性,用默认的也可以,但是如果有,就需要自己写了。至于这个涉及到深浅临摹的知识怎么写,这里就不说了。这条规则适用于大多数情况。

  自定义类型的构造函数呢?

  这个就不放动画了。像构造函数和析构函数一样,它寻找自定义类型的构造函数。

  赋值运算符重载

  本来想分两部分写的。这也是一大内容。我们可以为赋值操作符创建自己的实现规则。让我们先来看看什么是赋值运算符重载。

  引入了运算符重载来增强代码的可读性。运算符重载是一个具有特殊函数名的函数,它也有自己的返回值类型、函数名和参数列表。它的返回值类型和参数列表与普通函数相似。

  原型:返回值类型运算符运算符(参数列表)

  为什么赋值运算符会重载?

  我们先来看一个应用。

  为什么会报错?我只是想让他们比较一下。我怎么了?但是编译器不允许。今天,我必须让它允许我。这就是赋值运算符重载出现的原因。

  上课日期

  公共:

  日期(整数年=1900,整数月=1,整数日=1)

  _year=年份;

  _month=月;

  _day=天;

  私人:

  int _ year

  int _ month

  int _ day

  int main()

  日期d1(2022,5,18);

  日期d2(2022,5,18);

  如果(d1==d2)

  cout == endl

  返回0;

  }

  赋值运算符重载

  不用多说,现在就来看看怎么让它变得合理。

  我们来看看这个函数。现在有一个问题。我们无法获得该类的属性。它被封装了。先把属性改成public,再解决这个问题。

  布尔运算符==(日期d1,日期d2)

  //在这里,年、月、日是相等的。

  返回d1。_year==d2。_年

  d1。_month==d2。_月

  d1。_day==d2。_ day

  }

  就是这样。让我们调用这个函数。

  int main()

  日期d1(2022,5,18);

  日期d2(2022,5,18);

  if(运算符==(d1,d2))

  cout == endl

  返回0;

  }

  我们可能会想,我可以随便取个函数名就把这个函数的函数写出来,而且还是那么花哨,但是你写的函数可以这样调用吗?但是我的也可以。

  如果(d1==d2)

  cout == endl

  }

  这就是重载这个运算符的魅力所在。现在需要完善这个函数,参考一下。不需要开辟空间。用const修饰,避免被意外修改。

  布尔运算符==(常数日期d1,常数日期d2)

  //在这里,年、月、日是相等的。

  返回d1。_year==d2。_年

  d1。_month==d2。_月

  d1。_day==d2。_ day

  }

  解决无法获取属性的问题。

  我给出两种解决方案,一种是在类中写一些get函数来获取这些属性的值,另一种是使用友元,但是这种方法破坏了封装,不推荐。

  在类中写入运算符重载。

  我们还不如直接把这个函数写在类里。简单快捷,避免破包。

  上课日期

  公共:

  日期(整数年=1900,整数月=1,整数日=1)

  _year=年份;

  _month=月;

  _day=天;

  布尔运算符==(常数日期d1,常数日期d2)

  //在这里,年、月、日是相等的。

  返回d1。_year==d2。_年

  d1。_month==d2。_月

  d1。_day==d2。_ day

  公共:

  int _ year

  int _ month

  int _ day

  };

  你为什么报告这个错误?参数不是很对吗?我们之前说过,编译器默认会增加一个这个指针类型的参数,而且==是两个操作数,所以参数太多了。我们可以减少一个参数。

  boo operator==(const date d)//默认添加一个this指针

  //在这里,年、月、日是相等的。

  return _ year==d. _ year

  _ month==d. _ month

  _ day==d. _ day

  }

  这个函数的调用就变成这样了。

  int main()

  日期d1(2022,5,18);

  日期d2(2022,5,18);

  If (d1==d2) //d1==d2变成d1.operator==(d2)默认情况下

  cout == endl

  返回0;

  }

  代码里你说变成d1.operator==(d2),所以变成这样?你能证明什么?这里用对象的地址来证明。

  上课日期

  公共:

  日期(整数年=1900,整数月=1,整数日=1)

  _year=年份;

  _month=月;

  _day=天;

  布尔运算符==(常数日期d)

  cout“this”this endl;

  返回true

  公共:

  int _ year

  int _ month

  int _ day

  int main()

  日期d1(2022,5,18);

  日期d2(2022,5,18);

  cout d1 d1 endl

  cout d2 d2 endl

  如果(d1==d2)

  返回0;

  }

  重载运算符=

  本来想和大家分享一个约会类的,但是如果是此时此地写的话,至少需要5000字。我把它放在博客上,作为这些天我们学习课的一个小总结。在这节约会课上,你会发现我们上次讲过的所有知识点。这是一道小菜。这个很简单,目的是引出以下知识点。

  日期运算符=(常数日期d)

  如果(这个!=d)

  _ year=d. _ year

  _ month=d. _ month

  _ day=d. _ day

  返回* this

  }

  先称之为。

  int main()

  日期d1(2022,10,18);

  日期D2;

  d2=d1

  d2。print();

  返回0;

  }

  d2=d1

  细心的朋友可能会发现,我们写的是d2=d1我不想给d2赋值,而是想在这里重点关注它。我们通过调试来看看。

  这个电话是话务员超负荷。

  int main()

  日期d1(2022,10,18);

  日期D2;

  d2=d1

  d2。print();

  返回0;

  }

  日期d2=d1

  这调用复制构造,而不是运算符重载。

  int main()

  日期d1(2022,10,18);

  日期d2=d1

  d2。print();

  返回0;

  }

  从这里可以得出下一个结论。如果我们在定义变量并初始化它们的时候调用copy构造,如果在赋值的时候已经定义了两个变量,我们称之为操作符重载。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • office2010激活密钥大全 怎么永久激活office2010
  • project2010产品密钥免费_project2010激活密钥永久激活码
  • c语言调用退出函数 c语言退出整个程序怎么写
  • c语言中怎么给函数初始化 c语言的初始化语句
  • c语言编写函数计算平均值 c语言求平均函数
  • chatgpt是什么?为什么这么火?
  • ChatGPT为什么注册不了?OpenAI ChatGPT的账号哪里可以注册?
  • OpenAI ChatGPT怎么注册账号?ChatGPT账号注册教程
  • chatgpt什么意思,什么是ChatGPT ?
  • CAD中怎么复制图形标注尺寸不变,CAD中怎么复制图形线性不变
  • cad中怎么创建并使用脚本文件,cad怎么运行脚本
  • cad中快速计算器的功能,cad怎么快速计算
  • cad中快速修改单位的方法有哪些,cad中快速修改单位的方法是
  • cad中心点画椭圆怎么做,cad轴测图怎么画椭圆
  • CAD中常用的快捷键,cad各种快捷键的用法
  • 留言与评论(共有 条评论)
       
    验证码: