【c++】cpp类和对象

目录

(1)类的封装

(2)对象的构造和析构

定义和调用说明

有参数的构造函数,构造函数重载,拷贝构造函数

匿名对象的去和留

类中嵌套对象的初始化与对象初始化列表

类中const常量初始化与对象初始化列表

(3)new和delete用法

(4)类的声明和实现分开

(5)static与静态成员变量(函数)

静态成员变量

静态成员函数

(6)c++编译器对成员变量和函数的处理机制

内存占用机制

this指针

⽤const限定this指针


(1)类的封装

封装的多层含义

  • 把属性和⽅法进⾏封装
  • 对属性和⽅法进⾏访问控制
  • 类的内部和类的外部
  • 类的访问控制关键字
    • public:  修饰的成员变量和函数,可以在类的内部和类的外部访问
    • private:  修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问
    • protected:  修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问,⽤ 在继承⾥⾯

案例:设计⽴⽅体类(cube),求出⽴⽅体的⾯积和体积。求两个⽴⽅体,是否相等(全局函数和成员函数都要实现)。

#include <iostream>

class Cube
{
public:
    void setA(int a){m_a = a;}
    void setB(int b){m_b = b;}
    void setC(int c){m_c = c;}
    void setABC(int a = 0, int b = 0, int c = 0)
    {
        m_a = a;
        m_b = b;
        m_c = c;
    }
    int getA(){return m_a;}
    int getB(){return m_b;}
    int getC(){return m_c;}
public:
    int getV()
    {
        m_v = m_a * m_b * m_c;
        return m_v;
    }
    int getS()
    {
        m_s = 2 * (m_a * m_b + m_a * m_c + m_b * m_c);
        return m_s;
    }
    int judgeCube(Cube &v1, Cube &v2) //不建议这样写
    {
        if ((v1.getA() == v2.getA()) 
        && (v1.getB() == v2.getB()) 
        && (v1.getC() == v2.getC()))
            return 1;
        return 0;
    }
    int judgeCube(Cube &v2) //成员函数 函数重载
    {
        if ((m_a == v2.getA()) 
        && (m_b == v2.getB()) 
        && (m_c == v2.getC()))
            return 1;
        return 0;
    }
private:
    int m_a;
    int m_b;
    int m_c;
    int m_v;
    int m_s;
};

// 全局函数 PK 成员函数
// 1相等 0不相等
int judgeCube(Cube &v1, Cube &v2)
{
    if ((v1.getA() == v2.getA()) 
    && (v1.getB() == v2.getB()) 
    && (v1.getC() == v2.getC()))
        return 1;
    return 0;
}

int main()
{
    Cube v1, v2;
    v1.setABC(1, 2, 3);
    std::cout << v1.getS() << std::endl;
    std::cout << v1.getV() << std::endl;

    
    // 用成员函数判断两个立方体是否相等
    v2.setABC(1, 2, 4);
    if (v1.judgeCube(v1, v2) == 1)
        std::cout << "v1 = v2" << std::endl;
    else
        std::cout << "v1 != v2" << std::endl;

    // 用成员函数判断两个立方体是否相等
    v1.setABC(1, 2, 4);
    if (v1.judgeCube(v2) == 1)
        std::cout << "v1 = v2" << std::endl;
    else
        std::cout << "v1 != v2" << std::endl;

    // 用全局函数判断两个立方体是否相等
    if (judgeCube(v1, v2) == 1)
        std::cout << "v1 = v2" << std::endl;
    else
        std::cout << "v1 != v2" << std::endl;

    return 0;
}

(2)对象的构造和析构

定义和调用说明

1 构造函数定义

  • C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。
  • 构造函数默认是无参数的,当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。也可以在定义时自行设计有参数和函数体。
  • 没有任何返回类型的声明。

2 构造函数的调⽤

  • ⾃动调⽤:⼀般情况下C++编译器会⾃动调⽤构造函数
  • ⼿动调⽤:在⼀些情况下则需要⼿⼯调⽤构造函数

3 析构函数定义及调⽤

  • C++中的类可以定义⼀个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
  • 语法:~ClassName()
  • 析构函数没有参数也没有任何返回类型的声明
  • 析构函数在对象销毁时⾃动被调⽤
  • 析构函数调⽤机制:C++编译器⾃动调⽤

有参数的构造函数,构造函数重载,拷贝构造函数

⼆个特殊的构造函数:

  • 默认无参构造函数。当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。
  • 默认拷贝构造函数。当类中没有定义拷⻉构造函数时,编译器默认提供⼀个默认拷⻉构造函数,简单的进⾏成员变量的值复制。

示例代码

#include <iostream>
#include <cstring>

class Test
{
public:
    Test() //无参数构造函数
    {
        age = 0;
        strcpy(name, "tom");
    }
    Test(int age) //有参数构造函数 //构造函数重载
    {
        this->age = age;
        strcpy(name, "tom");
    }
    Test(int age, char *name) //有参数构造函数  //构造函数重载
    {
        this->age = age;
        strcpy(this->name, name);
    }
    Test(const Test &obj) // 拷贝构造函数
    {
        this->age = obj.age;
        this->name = obj.name;
    }
    ~Test()  //析构函数
    {
        std::cout << "object destroyed" << std::endl;
    }
public:
    void printT()
    {
        std::cout << "name = " << name << std::endl;
        std::cout << "age = " << age << std::endl;
        std::cout << "---------------" << std::endl;
    }
private:
    char *name = new char[20];
    int age;
};



int main()
{
    // 1.c++编译器自动的调用构造函数
    Test t0; // 自动调用无参数构造函数
    t0.printT();
    char name1[] = "hello";
    Test t1(10, name1); // 自动调用参数构造函数
    t1.printT();

    // 2.手动调用构造函数
    char name2[] = "world";
    Test t2 = Test(100, name2); 
    t2.printT();
    strcpy(name2, "good");
    Test t3 = Test(1000, name2); //t4对象的初始化
    t2 = t3;
    t2.printT();

    // 3.对象赋值
    t0 = t1; 
    std::cout << &t0 << "," << &t1 << std::endl;  // 不同
    t0.printT();
    //对象的初始化 和 对象的赋值 是两个不同的概念

    // 4.拷贝构造函数
    Test t4 = Test(t3);
    //Test t4 = t3; // 等价的方式 // 对象的初始化
    t4.printT();
    std::cout << &t3 << "," << &t4 << std::endl; // 不同

    return 0;
}

编译运行

[root@centos1 test]# g++ main.cpp -o main -std=c++11
[root@centos1 test]# ./main
name = tom
age = 0
---------------
name = hello
age = 10
---------------
name = world
age = 100
---------------
name = good
age = 1000
---------------
0x7ffd41eaa4e0,0x7ffd41eaa4c0
name = hello
age = 10
---------------
name = good
age = 1000
---------------
0x7ffd41eaa490,0x7ffd41eaa480
object destroyed
object destroyed
object destroyed
object destroyed
object destroyed
[root@centos1 test]# 

匿名对象的去和留

可以用函数返回对象,为匿名对象。关于匿名对象的去和留,结论如下:

  • 返回的匿名对象,若没人接盘,则这个匿名对象构造后会直接析构。
  • 返回的匿名对象,若赋值给另外一个同类型的对象,那么匿名对象在构造后会被析构。
  • 返回的匿名对象,若直接来初始化另外一个同类型的对象,那么匿名对象会直接转成新的对象。

示例代码

#include <iostream>

class Location
{
public:
    Location(int xx = 0, int yy = 0)
    {
        X = xx;
        Y = yy;
        std::cout << "Object Created\n";
    }
    Location(const Location &p) //拷贝构造函数
    {
        X = p.X;
        Y = p.Y;
        std::cout << "Object Copy" << std::endl;
    }
    ~Location()
    {
        std::cout << "(" << X << "," << Y << ") "
                  << "Object Destroyed" << std::endl;
    }
public:
    int GetX() { return X; }
    int GetY() { return Y; }
private:
    int X, Y;
};

void PrintLocation(Location p) //打印对象的属性
{
    std::cout << "Funtion:" << p.GetX() << "," << p.GetY() << std::endl;
}

Location GenLocation() //返回一个匿名对象
{
    Location A(1, 2);
    return A;
}

// 匿名对象的去和留,关键看,返回时如何接盘
// = 在赋值和初始化这两种情况时有所不同
int main()
{
    // (1)若返回的匿名对象,赋值给另外一个同类型的对象,那么匿名对象会被析构
    std::cout << "--------B--------" << std::endl;
    Location B;  // 调用一次构造函数
    B = GenLocation(); // 调用构造函数  // 然后调用析构函数
    

    //(2)若返回的匿名对象,直接来初始化另外一个同类型的对象
    // 那么匿名对象会直接转成新的对象
    std::cout << "--------C--------" << std::endl;
    Location C = GenLocation();  // 调用一次构造函数
    

    //(3)没人接盘 //这个匿名对象构造后会直接析构
    std::cout << "----------------" << std::endl;
    GenLocation(); // 调用一次构造函数  // 然后调用析构函数
    

    std::cout << "--------end--------" << std::endl;
    // 程序结束时
    // B调用一次析构函数
    // C调用一次析构函数

    return 0;
}

编译运行

[root@localhost test]# g++ main.cpp -o main
[root@localhost test]# ./main
--------B--------
Object Created
Object Created
(1,2) Object Destroyed
--------C--------
Object Created
----------------
Object Created
(1,2) Object Destroyed
--------end--------
(1,2) Object Destroyed
(1,2) Object Destroyed
[root@localhost test]# 

类中嵌套对象的初始化与对象初始化列表

情况描述:如果我们设计的类中有一个成员,它本身是另一个类,而且这个成员构造函数只有带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。

解决办法:C++在构造函数中提供初始化列表对成员变量进行初始化。

语法规则作用域::类名(参数1, 参数2, ...):成员类名1(实参值a, 实参值b,...), 成员类名2((实参值m, 实参值n,...)

构造函数和析构函数调用顺序

  • 构造函数顺序:先执行被组合对象的构造函数 。如果组合对象有多个,按照定义顺序,,而不是按照初始化列表的顺序。初始化列表先于构造函数的函数体执行。
  • 析构函数 : 和构造函数的调用顺序相反

示例:在B类中,组合了一个 A类对象,A类设计了构造函数,但构造函数只有带参的构造函数,根据构造函数的调用规则,必须要调用A类的带参构造函数。

示例代码

#include <iostream>
#include <cstring>

class A
{
public:
    A(int _a)
    {
        a = _a;
        std::cout << "generate A " << a << std::endl;
    }
    ~A()
    {
        std::cout << "destroy A " << a << std::endl;
    }
protected:
private:
    int a;
};

class B
{
public:
    //B(int _b1, int _b2): a1(_b1), a2(_b2)
    B(int _b1, int _b2): a1(1), a2(2)
    {
        b1 = _b1;
        b2 = _b2;
        std::cout << "generate B " << std::endl;
    }
    B(int _b1, int _b2, int m, int n):a1(m), a2(n)
    {
        b1 = _b1;
        b2 = _b2;
        std::cout << "generate B " << std::endl;
    }
    ~B()
    {
        std::cout << "destroy B " << std::endl;
    }
protected:
private:
    int b1;
    int b2;
    A a2;
    A a1;
};

void objplay()
{
    B(1, 2);
}

int main()
{
    objplay();
    return 0;
}

运行结果

generate A 2
generate A 1
generate B 
destroy B 
destroy A 1
destroy A 2

类中const常量初始化与对象初始化列表

当类成员中含有⼀个const对象时,或者是⼀个引⽤时,他们也必须要通过成员初始化列表进⾏初始化,因为这两种对象要在声明后⻢上初始化,⽽在构造函数中,做的是对他们的赋值,这样是不被允许的。

示例代码

#include <iostream>
#include <cstring>

class A
{
public:
    A(int _a)
    {
        a = _a;
        std::cout << "generate A " << a << std::endl;
    }
    ~A()
    {
        std::cout << "destroy A " << a << std::endl;
    }
public:
    int geta(){return a;}
protected:
private:
    int a;
};

class B
{
public:
    B(int _b1, int _b2): a1(_b1), a2(_b2), c(_b2)
    // B(int _b1, int _b2): a1(1), a2(2), c(0)
    {
        b1 = _b1;
        b2 = _b2;
        std::cout << "generate B " << std::endl;
    }
    B(int _b1, int _b2, int m, int n):a1(m), a2(n), c(0)
    {
        b1 = _b1;
        b2 = _b2;
        std::cout << "generate B " << std::endl;
    }
    ~B()
    {
        std::cout << "destroy B " << std::endl;
    }
public:
    void print()
    {
        std::cout << "---------" << std::endl;
        std::cout << b1 << std::endl;
        std::cout << b2 << std::endl;
        std::cout << a2.geta() << std::endl;
        std::cout << a1.geta() << std::endl;
        std::cout << c << std::endl;
    }
protected:
private:
    int b1;
    int b2;
    A a2;
    A a1;
    const int c;
};

void objplay()
{
    B obj_b1 = B(1, 2);
    obj_b1.print();

    B obj_b2(3, 4);
    obj_b2.print();
}

int main()
{
    objplay();
    return 0;
}

运行结果

generate A 2
generate A 1
generate B 
---------
1
2
2
1
2
generate A 4
generate A 3
generate B 
---------
3
4
4
3
4
destroy B 
destroy A 3
destroy A 4
destroy B 
destroy A 1
destroy A 2

(3)new和delete用法

在软件开发过程中,常常需要动态地分配和撤销内存空间。在C语⾔中是利⽤库函数malloc和 free来分配和撤销内存空间的。C++提供了较简便⽽功能较强的运算符new和delete来取代 malloc和free函数。

注意:new和delete是运算符,不是函数,因此执⾏效率⾼。

虽然为了与C语⾔兼容,C++仍保留malloc和free函数,但建议⽤户不⽤malloc和free函数,⽽ ⽤new和delete运算符。

new用法举例

  • new int;  //开辟⼀个存放整数的存储空间,返回⼀个指向该存储空间的地址(即指针)
  • new int(100);  //开辟⼀个存放整数的空间,并指定该整数的初值为100,返回⼀个指向该 存储空间的地址
  • new char[10];  //开辟⼀个存放字符数组(包括10个元素)的空间,返回⾸元素的地址
  • new int[5][4];  //开辟⼀个存放⼆维整型数组(⼤⼩为5*4)的空间,返回⾸元素的地址
  • float *p=new float(3.14159);  //开辟⼀个存放单精度数的空间,并指定该实数的初值为 3.14159,将返回的该空间的地址赋给指针变量p
  • Box *pt;  //定义⼀个指向Box类对象的指针变量pt
  • pt=new Box;  //在pt中存放了新建对象的起始地址
  • Box *pt = new Box ;
  • Box *pt=new Box(12,15,18);

注意⽤new分配数组空间时不能指定初值。如果由于内存不⾜等原因⽽⽆法正常分配空间,则new会 返回⼀个空指针NULL,⽤户可以根据该指针的值判断分配空间是否成功。

delete用法

若P是⼀个指向数组的指针,则delete p表示清空内存⾸地址,delete[] p表示清空整个数组 对应的堆中内存空间。

malloc-free和new-delete区别

  • malloc不会调⽤类的构造函数,new能执⾏类型构造函数
  • free不会调⽤类的析构函数,delete操作符 能执⾏类的析构函数

示例代码

#include <iostream>
#include <stdlib.h>

class Test
{
public:
    Test(int _a)
    {
        a = _a;
        std::cout << "generate A" << std::endl;
    }
    ~Test()
    {
        std::cout << "destroy A" << std::endl;
    }
protected:
private:
        int a;
};

int main()
{
    // 1.基础类型
    // 用malloc这个函数
    int *p1 = (int *)malloc(sizeof(int));
    *p1 = 10;
    free(p1);

    // 用new这个运算符
    int *p2 = new int;
    *p2 = 20;
    free(p2);

    int *p3 = new int(30);
    delete p3;

    // 2.数组
    // malloc
    int *p4 = (int *)malloc(sizeof(int) * 10); // int array[10];
    p4[0] = 1;
    free(p4);

    // new
    int *pArray = new int[10];
    pArray[1] = 2;
    delete[] pArray; 

    char *pArray2 = new char[25];
    delete[] pArray2;

    // 3.给对象分配堆内内存空间
    std::cout << "---------malloc------------" << std::endl;
    Test *testp1 = (Test *)malloc(sizeof(Test));
    std::cout << "---------free------------" << std::endl;
    free(testp1);

    std::cout << "----------new-----------" << std::endl;
    Test *testp2 = new Test(1);
    std::cout << "----------delete-----------" << std::endl;
    delete testp2;

    return 0;
}

运行结果

---------malloc------------
---------free------------
----------new-----------
generate A
----------delete-----------
destroy A

(4)类的声明和实现分开

设计.h和.cpp,一个声明,一个实现。为了避免同⼀个头⽂件被include多次带来重复编译,头⽂件要么用#ifndef 宏名  #define 宏名  #endif编写头文件,要么用#pragma once。#ifndef由语⾔⽀持移植性更好,推荐使用。

案例:带有数组成员的类

myarrray.h

#ifndef _MYARRAY_H_
#define _MYARRAY_H_

class Array
{
public:
    Array(int length);
    Array(const Array &obj); //加了const更好
    ~Array();
public:
    void setdata(int index, int value);
    int getdata(int index);
    int getLenth();
private:
    int m_length;
    int *m_space;
};

#endif

myarray.cpp

#include <iostream>
#include "myarray.h"

//作用域不要忘写
//构造函数
Array::Array(int length)  
{
    if (length < 0){length = 0;}
    m_length = length;
    m_space = new int[length];
    // this->m_length = length;
    // this->m_space = new int[length];
}
//构造函数的重载
Array::Array(const Array &obj) 
{
    m_length = obj.m_length;
    m_space = new int[m_length];
    for (int i = 0; i < m_length; i++)
        m_space[i] = obj.m_space[i];
}
//析构函数
Array::~Array()
{
    if (m_space != NULL)
    {
        delete[] m_space;
        m_space = NULL;
        m_length = -1;
    }
}
//成员函数
void Array::setdata(int index, int value)
{
    if (0 <= index && index <= m_length)
        m_space[index] = value;
}
int Array::getdata(int index)
{
    if (0 <= index && index <= m_length)
        return m_space[index];
    return -1;
}
int Array::getLenth()
{
     return m_length;
}

main.cpp

#include <iostream>
#include "myarray.h"

int main()
{
    Array array1(4);
    for (int i = 0; i < array1.getLenth(); i++)
    {
        array1.setdata(i, i + 100);
        std::cout << array1.getdata(i) << "\t";
    }
    std::cout << std::endl;

    Array array2 = array1;
    for (int i = 0; i < array2.getLenth(); i++)
    {
        std::cout << array2.getdata(i) << "\t";
    }
    std::cout << std::endl;

    return 0;
}

编译运行

[root@centos1 test]# g++ -o main main.cpp myarray.cpp
[root@centos1 test]# ./main
100     101     102     103
100     101     102     103
[root@centos1 test]# 

(5)static与静态成员变量(函数)

静态成员变量

思考:每个变量,拥有属性。有没有⼀些属性,归所有对象拥有?

把⼀个类的成员修饰为 static 时,这个类⽆论有多少个对象被创建,这些同类对象共享这个 static 成员。

  • 静态成员在类的内部声明,但要在类的外部初始化。静态成员局部于类,它不是对象成员。
  • 静态不初始化时,只要不调⽤这个静态成员编译就不会报错,但只有调⽤时才会报错。
  • 静态成员只有在初始化后,才会分配内存空间。
#include <iostream>

class counter
{
public:
    static int num; //声明静态数据成员
public:
    void setnum(int i) { num = i; } //成员函数访问静态数据成员
    void shownum() { std::cout << num << std::endl; }
};

// 在类的外部初始化静态数据成员
// 只能初始化一次
int counter::num = 0; 

int main()
{
    counter a, b;

    //调用成员函数访问私有静态数据成员
    a.shownum(); //0
    b.shownum(); //0

    //只要有一个对象改变了静态成员的值
    //则所有对象调用该静态成员时的值也会变
    a.setnum(10);
    a.shownum(); //10
    b.shownum(); //10

    //若静态数据成员是public的
    //还可以直接通过类访问静态成员
    std::cout << counter::num << std::endl;

    return 0;
}

静态成员函数

用static修饰成员函数,成为静态函数。

对于静态成员函数,是全部对象公有函数,提供不依赖于类数据结构的共同操作,它没有this指针。

  • 在类外调⽤静态成员函数⽤ “类名::静态函数”作限定词,或通过对象调⽤。
  • 注意静态函数中,不能使用普通成员变量,也不能调用普通成员函数,只能使用静态成员变量

#include <iostream>

class Worker
{
public:
    void company_age_add(){company_age++;}
    void print_company_age1()
    {
        std::cout << "normal print: company_age=" << company_age << std::endl;
    }
    static void print_company_age2() //静态成员函数
    {
        std::cout << "static print: company_age=" << company_age << std::endl;

        // std::cout << "age:" << age << std::endl; 
        // error C2597: 对非静态成员“Worker::age”的非法引用
        // 静态函数中 不能使用 普通成员变量 普通成员函数
    }
protected:
private:
    int age;
    int salary;
    static int company_age; //静态成员变量
};


int Worker::company_age = 10;

int main()
{
    Worker w1, w2, w3;
    // 1.普通成员函数调用静态成员变量
    w1.print_company_age1(); // normal print: company_age=10
    
    w2.company_age_add();
    w3.print_company_age1(); // normal print: company_age=11

    // 2.调用静态成员函数
    // 第一种:用对象.
    w3.print_company_age2();  // static print: company_age=11 
    // 第二种:类::
    Worker::print_company_age2(); // static print: company_age=11 

    return 0;
}

(6)c++编译器对成员变量和函数的处理机制

内存占用机制

C++类对象中的成员变量和成员函数是分开存储的。

  • 成员变量:
    • 普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对⻬⽅式
    • 静态成员变量:存储于全局数据区中
  • 成员函数:存储于代码段中。
#include <iostream>
#include <stdio.h>

class C1
{
public:
    int i; // 4个字节
    int j; // 4个字节
    int k; // 4个字节
protected:
private:
}; //共12个字节

class C2
{
public:
    int i;
    int j;
    int k;
    static int m;
public:
    int getK() const { return k; }  
    void setK(int val) { k = val; } 
protected:
private:
};

struct S1
{
    int i;
    int j;
    int k;
}; // 12

struct S2
{
    int i;
    int j;
    int k;
    static int m;
}; // 12


int main()
{
    printf("c1:%d \n", sizeof(C1)); //12
    printf("c2:%d \n", sizeof(C2)); //12
    printf("s1:%d \n", sizeof(S1)); //12
    printf("s2:%d \n", sizeof(S2)); //12
    return 0;
}

this指针

C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。

静态成员函数与普通成员函数的区别:静态成员函数不包含指向具体对象的指针,普通成员函数 包含⼀个指向具体对象的指针。

#include <iostream>

class Test
{
public:
    Test(int a, int b)
    {
        this->a = a;
        this->b = b;
    }

    void print1(int a) //这个a是函数形参的a
    {
        std::cout << "----------print1----------" << std::endl;
        for (int i = 0; i < a; i++)
        {
            std::cout << "a: " << this->a << std::endl; //打印对象的属性a的值
            std::cout << "b: " << this->b << std::endl;
        }
    }

    void print2() 
    {
        std::cout << "----------print2----------" << std::endl;
        // 当函数形参没有与成员变量同名的形参时
        // 函数内部可以不用this,直接访问成员变量
        std::cout << "a: " << a << std::endl; //打印对象的属性a的值
        std::cout << "b: " << b << std::endl;

    }

protected:
private:
    int a;
    int b;
};

int main()
{
    Test t1(1, 2);
    t1.print1(3);

    t1.print2();

    return 0;
}

⽤const限定this指针

C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。

将const写在成员函数处,比如void OperateVar(int a, int b) const,将会对this指针和this下的成员进行修饰,只有只读属性,使得this和this成员不能被改变。

注意:

  • const 无论写在函数什么位置,都没有关系,像下面这样都可以
    • void OperateVar(int a, int b) const

    • const void OperateVar(int a, int b)

    • void const OperateVar(int a, int b)

  • const修饰的是谁?

    • const修饰的是形参a吗?不是

    • const修饰的是属性this->a  this->b

    • const修饰的是this指针所指向的内存空间, 修饰的是this指针

#include <iostream>

class Test
{
public:
    Test(int a, int b) //---> Test(Test *this, int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    void printT()
    {
        std::cout << "a: " << a << "\t";
        std::cout << "b: " << this->b << std::endl;
    }

    void OperateVar(int a, int b) const
    // void const OperateVar(int a, int b)  
    // const void OperateVar(int a, int b)
    {
        a = 100;
        // this->a = 100; //因为const,会报错
        // this->b = 200; //因为const,会报错
        // this = 0x11;   //因为const,会报错
        std::cout << "a: " << this->a << "\t";
        std::cout << "b: " << this->b << std::endl;
    }
protected:
private:
    int a;
    int b;
};

int main()
{
    int *m_space = new int[0];
    if (m_space == NULL)
    {
        return -1;
    }

    Test t1(1, 2);
    t1.printT();
    //a: 1  b: 2

    t1.OperateVar(100, 200);
    //a: 1  b: 2

    return 0;
}

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/582607.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QFileDialog窗口没有文件选择路径框问题的处理方法

QFileDialog作为QT自带的文件对话框&#xff0c;其界面有挑选文件路径的区域 但在某些操作系统下&#xff08;如欧拉操作系统&#xff09;&#xff0c;文件挑选框QFileDialogLineEdit可能会隐藏&#xff0c;导致无法选择文件路径 解决方法&#xff1a; QFileDialog* fd; fd-&…

【团体程序设计天梯赛】往年关键真题 L2-026 小字辈 递归 L2-027 名人堂与代金券 排序 详细分析完整AC代码

【团体程序设计天梯赛 往年关键真题 详细分析&完整AC代码】搞懂了赛场上拿下就稳 【团体程序设计天梯赛 往年关键真题 25分题合集 详细分析&完整AC代码】&#xff08;L2-001 - L2-024&#xff09;搞懂了赛场上拿下就稳了 【团体程序设计天梯赛 往年关键真题 25分题合…

2024最新docker部署gitlab

docker部署gitlab 快速命令 1 拉取镜像 docker pull gitlab/gitlab-ce2 启动容器 docker run -itd \-p 9980:80 \-p 9922:22 \-v /opt/soft/docker/gitlab/etc:/etc/gitlab \-v /opt/soft/docker/gitlab/log:/var/log/gitlab \-v /opt/soft/docker/gitlab/opt:/var/opt/g…

Xinlinx FPGA如何降低Block RAM的功耗

FPGA中降低Block RAM的功耗有两种方式&#xff0c;分别是选择合适的写操作模式以及Block RAM的实现算法及综合设置。我们知道对于采用IP核生成对应的RAM时&#xff0c;会有最小面积算法、低功耗算法以及固定原语&#xff0c;但是采用最小功耗算法有时由于级联长度导致无法实现&…

1 集成学习基础

目录 0 简述 1 集成学习算法代表 1.1 Bagging 1.1.1 模型预测的结果组合的方式 1.2 stacking 1.3 blending和stacking优缺点对比 0 简述 集成学习&#xff0c;典型的群殴策略&#xff0c;但是如何组织让彼此配合得当发挥最大的价值是一个值得思考的问题。 集成学习是一…

MySQL-笔记-08.数据库编程

目录 8.1 编程基础 8.1.1 基本语法 8.1.2 运算符与表达式 1. 标识符 2. 常量 &#xff08;1&#xff09; 字符串常量 &#xff08;2&#xff09;日期时间常量 &#xff08;3&#xff09;数值常量 &#xff08;4&#xff09;布尔值常量 &#xff08;5&#xff09;NULL…

2024长三角快递物流展:科技激荡,行业焕发新活力

7月8日&#xff0c;杭州将迎来快递物流科技盛宴&#xff0c;这是一年一度的行业盛会&#xff0c;吸引了全球领先的快递物流企业和创新技术汇聚一堂。届时&#xff0c;会展中心将全方位展示快递物流及供应链、分拣系统、输送设备、智能搬运、智能仓储、自动识别、无人车、AGV机器…

判断前端入参是否空否则提示前端写法

vue2中 前端先声明一个变量&#xff0c;用于alert判断 在templeat中定义一个提示语句 然后在点击事件时判断一下是否展示

API接口知识小结

应用程序接口API&#xff08;Application Programming Interface&#xff09;&#xff0c;是提供特定业务输出能力、连接不同系统的一种约定。这里包括外部系统与提供服务的系统&#xff08;中后台系统&#xff09;或后台不同系统之间的交互点。包括外部接口、内部接口&#xf…

串联超前及对应matlab实现

串联超前校正它的本质是利用相角超前的特性提高系统的相角裕度。传递函数为&#xff1a;下面将以一个实际的例子&#xff0c;使用matlab脚本&#xff0c;实现其校正后的相位裕度≥60。

在VSCode中调试其他软件执行的python文件

在VSCode中调试其他软件执行的python文件 0. 实际场景 我有一段python代码想在Metashape中运行&#xff0c;但是又想在中间某一步停下来查看变量值。由于Metashape的python环境不容易在vscode中配置&#xff0c;所以直接用vscode调试单个文件的方式无法实现这个想法。还好&am…

Java 四大名著之一,豆瓣9.7,Java神作重磅上市

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星评选TOP 10&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作…

PG修改端口号与error: could not connect to server: could not connect to server 问题解决

刚开始学习PG修改端口号之后数据库端口号没变。 修改端口号&#xff1a;/usr/local/pgsql/data中的postgresql.conf中 修改后并不能直接生效需要重启PG&#xff1a; /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l /usr/local/pgsql/data/logfile restart重启后新…

R语言--图形绘制

一&#xff0c;绘制简单图形 c1<- c(10,20,30,40,50) c2<-c(2,7,15,40,50) plot(c1,c2,typeb) 具体参数请参考R语言中的绘图技巧1&#xff1a;plot()函数参数汇总_r语言plot参数设置-CSDN博客 c1<- c(10,20,30,40,50) c2<-c(2,7,15,40,50) plot(c1,c2,typeb,col#…

【上岗认证】错题整理记录

目录 &#x1f31e;一、阶段1&#xff1a;编码规范 &#x1f30a;编码规范考试-CC &#x1f31e;二、阶段2&#xff1a;开发基础 &#x1f30a;C/C &#x1f30a;数据库&#xff08;Oracle/MySql&#xff09; &#x1f31e;三、阶段3&#xff1a;测试基础 &#x1f30a;…

智慧校园研究新发展

随着新兴信息技术出现&#xff0c;智慧校园研究发展出新的样态。智慧校园研究新进展主要体现在信息化背景下的未来教育理论发展&#xff0c;以新一代人工智能技术为代表的新兴技术应用&#xff0c;以及“人—技”协作的个性化学习创新。 智慧校园是未来教育的重要入口&#xff…

WPS的JS宏如何设置Word文档的表格的单元格文字重新编号

希望对Word文档中的表格进行统一处理&#xff0c;表格内的编号&#xff0c;有时候会出现紊乱&#xff0c;下一个表格的编号承接了上一个表格的编号&#xff0c;实际需要重新编号。 当表格比较多时&#xff0c;手动更改非常麻烦&#xff0c;而且更改一遍并不能完成&#xff0c;…

vue2—— mixin 超级详细!!!

mixin Mixin是面向对象程序设计语言中的类&#xff0c;提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类 Mixin类通常作为功能模块使用&#xff0c;在需要该功能时“混入”&#xff0c;有利于代码复用又避免了多继承的复杂 vue中的mixin 先来看一下官方定义 mi…

echarts特殊处理(滚动条、legend分页、tooltip滚动)

当图表数据量过大时&#xff0c;为了使用者能够有更好的体验&#xff0c;对于大数据量的图表处理&#xff1a; 1、当x轴数据过多不能完全展示时&#xff0c;需要添加滚动条&#xff1a;option设置dataZoom字段 dataZoom: [{ // 这部分是关键&#xff0c;设置滚动条type: slide…

实验案例二:配置Trunk,实现相同VLAN的跨交换机通信

实验环境 公司的员工人数已达到 100 人&#xff0c;其网络设备如图12.13所示&#xff0c;现在的网络环境导致广播较多网速慢&#xff0c;并且也不安全。公司希望按照部门划分网络&#xff0c;并且能够保证一定的网络安全性 其网络规划如下、 PC1和 PC3为财务部&#xff0c;属于…
最新文章