C++ 类总结(构造析构函数、初始化列表、this指针、静态成员和常对象)

一、类(class)

(1)类的定义

定义类:一般来说,类规范由两个部分组成。

  • 类声明:以数据成员的方式描述数据部分,以成员函数的方式描述公共接口
  • 类方法定义:描述如何实现类成员函数
1.C++中的class与struct一样,是一种复合数据类型
2.class里面用变量来表达属性,用函数表示行为
3.在C++中class与struct几乎没有区别,struct中默认访问属性是public(公开),class中默认访问属性是private(私有),事实上还有第三个访问控制关键字protected

(2)类成员函数

成员函数定义与常规函数定义相同,他们有函数头和函数体,也可以有返回类型和参数,但是它们有两个特征:

  • 在类中声明成员函数,类外实现成员函数时,要使用作用域解析运算符(::)来标识函数所属的类;
  • 类方法可以访问类的private组件(如果试图使用非类成员函数访问这些数据成员,编译器会禁止这样做,但友元函数例外)
class Stu
{
private:
    char* name;
    short age;
public:
      void show(void);//方法1的函数声明
    /*void show(void)//方法2
       {
               ;
       }*/
};
void Stu::show(void)//方法1的函数实现
{ 
        ;
}
//两种方式都可以

二、class的构造函数

1.在创建对象时自动调用的函数,在整个对象的生命周期中一定会被调用一次,且只能被调用一次。
2.在构造函数中负责对成员变量的初始化,分配资源、设置对象的初始化状态

初始化方式:
  • classtype c1(“string”,b,c…);
  • classtype c2 = (“string”,b,c,…);
  • classtype* c3 = new class (“string”,b,c); / new class {“string”,b,c};
  • classtype c4 = c3;
  • classtype c5(c4); //注意:c4、c5调用的是拷贝构造函数,后面会讲
  • classtype c6;c6 = c3; //注意:c6调用的是赋值构造函数,后面也会讲

3.构造函数可以有多个版本,这些不同版本之间会构造成重载,创建对象时的方式不同、给的参数不同会调用不同的构造函数,如果调用的构造函数不存在会造成编译错误。
4.类型转换构造函数:用一个数据给对象初始化,默认自动调用构造函数,达到类型转换的效果。可以通过在构造函数前加关键字explicit 防止其隐式转换的方式调用构造函数。默认的构造函数是自动转换。

三、无参构造函数

1.如果类中没有定义构造函数,编译器会自动生成一个无参构造。一旦定义了其他版本的构造函数,无参构造就不会再生成了,因此为了防止无参方式创建对象出错,在定构造函数时,最好要定义两个构造函数。
2.无参构造未必是无参,因为在C++中函数可以有默认参数,如果有参构造函数设置了默认参数,那么就会和无参构造产生歧义,它们两个只能存在一个。

class Student
{
public:
 Student(void) {}//与下一句会产生歧义
 Student(int n=10) {std::cout << "have\n";}
 ~Student() {std::cout << "...\n";}
};

3.什么时候会调用无参构造

  • 3.1 Student stu <=> Student* stup = new Student;
  • 3.2 创建对象数组,每个对象都会调用一次无参构造
  • 3.3 如果类A中有成员是类B,当执行类A的构造函数前会先自动调用类B的无参构造
  • 3.4 在类A中如何调用类B的有参构造
A构造函数(参数列表):成员类B(参数列表)
{
     ·····
}

四、拷贝构造函数

1.是一种特殊的构造函数,就是用一个已有的对象去构造其同类的副本对象,即对象克隆。

class classname
{
private:
         int x;
         int y;
         ...
public:
        classname(classname &that)
        {
              x = that.x;
              y = that.y;     
                ...//逐个成员赋值
        }
}

2.编译器会默认生成一个拷贝构造函数

  • 编译生成的拷贝构造函数默认会逐字节复制类中的每一个成员
  • 如果类A中有类B成员,会在类A的拷贝构造中自动调用类B的拷贝构造函数

3.程序员可以自定义拷贝构造函数来取代默认的拷贝构造

  • a、拷贝构造只能有一个,不能重载
  • b、一旦程序员自定义了拷贝构造,编译器就不再生成
  • c、在自定义的拷贝构造中能通过编码来实现成员的复制

4.一般情况下编译器生成的拷贝构造函数完全够用,不要轻易自定义构造

5.什么情况下调用拷贝构造函数

  • a、对象需要通过另外一个对象进行初始化
  • b、对象以值传递的方式传入函数参数
  • c、对象以值传递的方式从函数返回
Student stu2 = stu1;Student stu2(stu1);
void func(Student stu);func(stu1);
Student func(void) { return *this;} Student stu = func()


举例:自定义一个拷贝构造函数来给学生类拷贝初始化,学生名字为char* 类型。代码

五、赋值构造函数

1、赋值构造就是一个对象给另一个对象赋值调用的构造函数

Student stu2 = stu1 //拷贝构造
Student stu2;stu2 = stu1;//赋值构造

2、编译器会默认生成赋值构造,它的功能与拷贝构造函数的功能一样,把对象A完全拷贝给对象B

3、赋值构造与拷贝构造的区别:

  • 拷贝构造:使用对象A去创建出对象B(调用时对象B还未生成)
  • 赋值构造:对象A与对象B都已经构造完成,此时B = A

    如果对象中有常成员,拷贝构造可以成功调用,但赋值构造不行。

4、一般情况下赋值构造函数完全够用,除非有成员是指针,指向了其他内存空间,那么就需要自定义赋值构造函数

5、自定义赋值构造

  • a、确定赋值构造的格式
  • b、防止自赋值( int num = 1;num = num;)
  • c、释放旧资源
  • d、分配新资源
  • e、拷贝新内容

六、初始化列表

1.是一种成员的初始化方式

class 类名
{
        类名(参数列表):成员1(参数1),成员2(参数2),成员3{成员3[1],成员3[2]...}...//成员3为数组的情况
        {
        }
}
  • a、参数列表可以解决构造函数的参数与成员重名的问题
  • b、参数列表会先于构造函数执行

2、如果类成员是数组,可以使用{ }进行初始化
3、如果有成员是类,可以在初始化列表中显示调用构造函数
4、如果成员中有const成员、引用成员,必须使用初始化列表
5、类成员的构造顺序与初始化列表无关,而是与成员定义的顺序有关

七、this指针

C++为成员函数提供了一个名字为this的指针,这个指针称为自引用指针。每当创建一个对象时,系统就把this指针初始化为指向该对象。每当调用一个成员函数时,系统就自动把this指针作为一个隐含的参数传给该函数。不同的对象调用同一个成员函数时,C++编译器将根据成员函数this指针所指向的对象来确定应该引用哪一个对象的数据成员。通常情况下,this指针是隐含存在的,也可以将其显示的表示出来。

1、相同类型的对象各自拥有独立的成员实例,彼此是共享一份成员函数,成员函数是如何知道谁在调用?

  • 答:为了让成员函数知道是哪个对象在调用,并准确访问到对象的成员,编译器会自动为每个成员函数添加一个看不到的参数,这个参数就是指向调用对象的指针(this)

2、类中的所有成员函数都有this指针,包括构造、拷贝构造、析构等(只是构造中this指向的是正在被创建的对象)。

3、this指针默认情况下都是隐藏的(在成员函数中访问成员变量时自动加上),但也可以显示使用。

4、什么情况使用this

  • a、区分成员变量与参数
  • b、把对象当做返回值与其他对象进行交互

注: this指针是一个const指针,不能在程序中修改它或给它赋值;

八、常对象与常函数

1.创建对象时添加const关键字,这个对象就不可再修改(整个对象的所有东西都不可更改),就有了常属性。
2.常对象不能调用普通成员函数,调用成员函数相当于把对象的this指针给了它,就会有被修改的风险。
3.函数体前,函数参数列表后加const关键字的函数叫常函数,常对象只能调用常函数,普通对象也可以调用常函数。常函数相当于对this指针添加了const属性。
4、常函数与非常函数会形成重载而不会冲突
5、如果常对象中有成员确实需要修改,可以在成员前添加关键字mutable,这样调用常函数就可以修改此成员

class name
{
public:
        func(void) const //常成员函数
        { }
        func(void)//普通成员函数(非常函数)
        { }
};

看个例子

#include <iostream>
#include <cstring>

using namespace std;

class Student
{
private:
 char name[20];
 const int id;
 mutable int age;//关键字 使得age可以修改
public:
 Student(const char* name,int id,int age):id(id),age(age)//初始化列表id和age
 {
      strcpy(this->name,name);
 }
 void age_add(void) const //常函数
 {
  age++;
 }
 void show(void) const //常函数
 {
  cout << name << " " << id << " " << age << endl;
 }
 ~Student() { }

};

int main(int argc, char const *argv[])
{
 const Student stu("Apple",1024,17);
 stu.age_add();
 stu.show();
 return 0;
}

返回赋值构造函数

九、析构函数

1、当成员被销毁时自动调用的函数叫析构函数,对象的整个生命周期中只能被调用一次,它是对象被销毁前的最后一个执行的动作。
2、编译器会默认产生一个析构函数,默认析构函数负责销毁能看得到的成员,如果有成员是类会自动调用该类成员的析构函数,成员的析构过程与构造过程顺序相反。
3、析构函数虽然不能重载,但可以自定义,有自定义的析构函数后默认的析构函数就不会生成。
4、当类中有析构函数看不到的资源时(new/malloc)、有需要还原的设置时(关闭打开的文件,保存获取的数据)就需要自定义析构函数。

十、静态成员与静态函数

1、类成员被static修饰后,就会存储在bss段(此段是由编译器存放且大小固定),在程序中动态创建对象时它的静态成员就无法创建,所有此类创建的对象共享一个静态成员
2、静态成员只能在类中声明不能在类中定义(必须在类外定义)

类中: static 类型 成员
类外:类型 类名 成员;

3、静态成员就是声明在类中的全局变量,在任何位置都可以使用

类名::静态成员名    //访问

4、静态成员函数,类中的成员函数被static修饰后就变成了静态成员函数,所有的对象共享一份静态成员函数。
5、静态成员函数不会传递this指针。不通过对象也能调用静态成员函数

类名::静态成员函数(参数);


学生类拷贝构造函数:

#include <iostream>
#include <cstring>

using namespace std;

class Student
{
private:
 char* name;
 char sex;
 short age;
public:
 Student(void) { }
 Student(const char* _name,char _sex,short _age)
 {
  name = new char[strlen(_name)+1];
  strcpy(name,_name);
  sex = _sex;
  age = _age;
  cout << "start" << endl;
 }
 Student(Student &that)
 {
  name = new char[strlen(that.name)+1];
  strcpy(name,that.name);
  sex = that.sex;
  age = that.age;
  cout << "copy" << endl;
 }
 void show(void)
 {
  cout << "name:" << name << " sex: " << sex << " age: " << age <<endl;
 } 
 ~Student()
 {
  delete [] name;
  cout << "end" << endl;
 }

};

int main(int argc, char const *argv[])
{
 Student stu1("K",'m',30);
 Student stu2(stu1);
 stu1.show();
 stu2.show();
 return 0;
}

返回

猜你喜欢

转载自blog.csdn.net/Canger_/article/details/81543039
今日推荐