八、引用

引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。eg:类似于给你取一个新的呼叫名,比如你叫李华,我可以叫你小华(当公司只有你一个带华字的),等于说我可以通过这个呼叫你(直接访问)。

 

引用相当于给这个内存中的数据提供了一个新的变量名。(我脑子存储了你叫小华)

 

引用很容易和指针混淆。

 

引用和指针的区别

1.不存在空引用。引用必须连接到一块合法的内存。

2.一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。

3.引用必须在创建时被初始化。指针可以在任何时间被初始化。

可以说变量名是变量在内存中的第一 个名字,对它的引用是其第二个名字;

int a=6; int* p=&a; *p=20;

可以为 a声明引用变量

int& r = a;
double& s = d;

&读作引用。第一个声明读作” r 是一个初始化为 i 的整型引用“,第二个同理。

特性引用 (Reference)指针 (Pointer)
声明语法int& ref = var;int* ptr = &var
是否可为空不可为空,必须绑定有效对象 可为 nullptr
是否可重新绑定一旦绑定,不可更改目标可随时指向其他变量或 nullptr
访问目标值直接使用 ref需解引用 *ptr
内存地址存储 不存储地址,是变量的别名存储变量的地

 

 

 

 

 

 

 

 

 

#include <iostream>
using namespace std;

int main() {
    int value = 10;

    // 引用部分
    int& ref = value;  // ref 是 value 的别名
    ref = 20;          // 修改 ref 等同于修改 value
    cout << "引用修改后 value = " << value << endl;  // 输出: 20

    // 尝试重新绑定引用(不允许!编译报错)
    // int otherValue = 30;
    // ref = otherValue;  // 这只是赋值,不是重新绑定!ref 仍是 value 的别名


    //  指针部分 
    int* ptr = &value;  // ptr 存储 value 的地址
    *ptr = 30;          // 修改指针指向的内容
    cout << "指针修改后 value = " << value << endl;  // 输出: 30

    // 重新指向另一个变量(允许)
    int newValue = 40;
    ptr = &newValue;    // ptr 现在指向 newValue
    *ptr = 50;          // 修改 newValue
    cout << "newValue = " << newValue << endl;  // 输出: 50

    // 空指针( 允许)
    ptr = nullptr;      // ptr 不指向任何对象
    // cout << *ptr;    // 不要这样做!会导致崩溃(解引用空指针)


    // 地址对比
    cout << "\n 内存地址: " << endl;
    cout << "value 地址: " << &value << endl;
    cout << "ref 地址: " << &ref << endl;     // 与 value 地址相同
    cout << "ptr 地址: " << &ptr << endl;     // 指针本身的地址
    cout << "ptr 指向的地址: " << ptr << endl; // 当前 ptr是nullptr

    return 0;
}

这是一个例子

#include<iostream>
using namespace std;

double val[]={10.1,12.6,33.1,24.1,50.0};

double& setValue(int i)
{
    double& ref = val[i];
    return ref;
}

int main()
{
    setValue (3) = 90.9;
    cout<< vals[3] ;
    return 0;
}

 

返回引用时,要注意被引用的对象不能超出作用域。所以返回一个局部变量的引用是不合法的。



int& fun(){

int q;
// return q; //编译发生错误
static int x;
return x; //安全,在函数作用域外依然有效

}

 

为什么通过函数可以修改数组?

核心原理:引用传递

  • setValue 返回的是 val[i] 的引用(double&),而不是它的拷贝。
  • 对引用的修改会直接影响原始数组元素,因为引用是原始数据的“别名”。
函数类型示例代码修改是否生效
返回引用double& setValue(int i)修改原始数组
返回值double setValue(int i)只修改函数内副本

指针与引用情话:我希望我们之间是引用的关系,而非指针。(让我们像引用一样,成为彼此生命中不可分割的别名(alias),而不是可以随时指向他人的指针。" 在代码世界里,引用是最深情的绑定方式,正如人间最动人的爱情——不需要多余的语法糖,只需要一次确定的绑定,便是一生的承诺。)哥们玩抽象的

技术层面:

引用(Reference):一旦初始化就无法改变指向,必须绑定到一个有效的对象,且生命周期与原对象一致。就像"我注定只能是你"。

指针(Pointer):可以随时改变指向,甚至可能悬空(dangling pointer)或指向null,象征关系的不确定性。

 

九、重载

函数重载

在同一个作用域内,可以声明几个功能类似的同名函数;

C++ 中,函数重载 是指 在同一作用域内,可以有多个同名函数,但它们的参数列表不同(参数的类型、个数或顺序不同)。编译器会根据调用时传入的实参来自动选择匹配的函数。

函数重载的规则:

  1. 函数名必须相同
  2. 参数列表必须不同(以下任一):
    • 参数个数不同
    • 参数类型不同
    • 参数顺序不同
  3. 返回类型可以不同,但不能仅靠返回类型不同来重载

 

实例代码:

1.参数个数不同

#include <iostream>
using namespace std;

// 计算两个数的和
int add(int a, int b) {
    cout << "调用两个参数的 add" << endl;
    return a + b;
}

// 计算三个数的和
int add(int a, int b, int c) {
    cout << "调用三个参数的 add" << endl;
    return a + b + c;
}

int main() {
    cout << add(2, 3) << endl;        // 调用 add(int, int)
    cout << add(2, 3, 4) << endl;     // 调用 add(int, int, int)
    return 0;
}

2.参数类型不同

#include <iostream>
using namespace std;

// 整数相加
void printSum(int a, int b) {
    cout << "整数和: " << a + b << endl;
}

// 浮点数相加
void printSum(double a, double b) {
    cout << "浮点数和: " << a + b << endl;
}

int main() {
    printSum(3, 5);        // 调用 int 版本
    printSum(3.5, 6.7);    // 调用 double 版本
    return 0;
}

3.参数顺序不同

#include <iostream>
using namespace std;

// 先 int 后 string
void show(int a, string s) {
    cout << "int: " << a << ", string: " << s << endl;
}

// 先 string 后 int
void show(string s, int a) {
    cout << "string: " << s << ", int: " << a << endl;
}

int main() {
    show(10, "Hello");           // 调用 show(int, string)
    show("World", 20);           // 调用 show(string, int)
    return 0;
}

4.结合默认参数(注意歧义)

#include <iostream>
using namespace std;

void greet(string name) {
    cout << "Hello, " << name << "!" << endl;
}

//  如果启用下面这行,会产生调用歧义!
// void greet(string name = "Guest") {
//     cout << "Hi, " << name << "!" << endl;
// }
//程序会不清楚应该调用哪一个,下面的含有默认的参数,应该输出哪一个

int main() {
    greet("Alice");  // 如果有两个 greet,编译器不知道选哪个
    return 0;
}

5.重载构造函数(类中的函数重载)

#include <iostream>
using namespace std;

class Rectangle {
private:
    int width, height;
public:
    // 构造函数1:无参
    Rectangle() {
        width = 1;
        height = 1;
        cout << "调用无参构造函数" << endl;
    }

    // 构造函数2:一个参数(正方形)
    Rectangle(int w) {
        width = w;
        height = w;
        cout << "调用单参构造函数" << endl;
    }

    // 构造函数3:两个参数
    Rectangle(int w, int h) {
        width = w;
        height = h;
        cout << "调用双参构造函数" << endl;
    }

    void showArea() {
        cout << "面积: " << width * height << endl;
    }
};

int main() {
    Rectangle r1;           // 调用 Rectangle()
    Rectangle r2(5);        // 调用 Rectangle(int)
    Rectangle r3(3, 4);     // 调用 Rectangle(int, int)

    r1.showArea();
    r2.showArea();
    r3.showArea();

    return 0;
}

十、构造函数

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造,构造的是什么:构造成员变量的初始值,内存空间等

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回void。构造函数可用于为某些成员变量设置初始值。

以下是一个实例化

#include <iostream>
#include <string>

// 1. 定义一个名为 Book 的类 (class)
class Book {
private:
    // 私有成员变量,用于存储书的属性
    std::string title;
    std::string author;
    int pages;
    bool is_borrowed;

public:
    // 2. 默认构造函数 (Default Constructor)
    // 当没有提供参数时调用此构造函数
    Book() {
        title = "未知标题";
        author = "未知作者";
        pages = 0;
        is_borrowed = false;
        std::cout << "调用了默认构造函数,创建了一本新书。\n";
    }

    // 3. 带参数的构造函数 (Parameterized Constructor)
    // 当提供书名和作者时调用此构造函数
    Book(const std::string& t, const std::string& a) {
        title = t;
        author = a;
        pages = 0; // 页数默认为0,可以在后续设置
        is_borrowed = false;
        std::cout << "调用了带参数的构造函数,创建了《" << title << "》。\n";
    }

    // 4. 带多个参数的构造函数 (Another Parameterized Constructor)
    // 当提供书名、作者和页数时调用此构造函数
    Book(const std::string& t, const std::string& a, int p) {
        title = t;
        author = a;
        pages = p;
        is_borrowed = false;
        std::cout << "调用了带参数的构造函数,创建了《" << title << "》,共 " << pages << " 页。\n";
    }

    // 5. 析构函数 (Destructor)
    // 对象销毁时自动调用
    ~Book() {
        std::cout << "《" << title << "》这本书被销毁了。\n";
    }

    // 6. 成员函数 (Member Functions) 用于操作对象的状态
    void borrow() {
        if (!is_borrowed) {
            is_borrowed = true;
            std::cout << "《" << title << "》已被借出。\n";
        } else {
            std::cout << "《" << title << "》已在借阅中。\n";
        }
    }

    void returnBook() {
        if (is_borrowed) {
            is_borrowed = false;
            std::cout << "《" << title << "》已归还。\n";
        } else {
            std::cout << "《" << title << "》未被借出。\n";
        }
    }

    // Const成员函数,用于获取对象状态,不修改对象
    void displayInfo() const {
        std::cout << "书名: " << title << "\n作者: " << author << "\n页数: " << pages << "\n状态: " << (is_borrowed ? "已借出" : "在馆") << "\n";
        std::cout << "------------------------\n";
    }
};

int main() {
    std::cout << "--- C++ 构造函数与对象实例化演示 ---\n\n";

    // 7. 实例化对象 (Instantiation)
    // 使用默认构造函数创建对象 book1
    std::cout << "创建 book1 (使用默认构造函数):\n";
    Book book1; // 不需要 'new',对象在栈上分配
    book1.displayInfo();

    // 使用带两个参数的构造函数创建对象 book2
    std::cout << "\n创建 book2 (使用带参数的构造函数):\n";
    Book book2("C++ Primer", "Stanley Lippman"); // 自动调用匹配的构造函数
    book2.displayInfo();

    // 使用带三个参数的构造函数创建对象 book3
    std::cout << "\n创建 book3 (使用带参数的构造函数):\n";
    Book book3("Effective C++", "Scott Meyers", 250);
    book3.displayInfo();

    // 演示对象方法的使用
    std::cout << "\n--- 演示对象方法 ---\n";
    book2.borrow();
    book2.displayInfo();

    book3.borrow();
    book3.borrow(); // 尝试再次借出
    book3.displayInfo();

    book2.returnBook();
    book2.displayInfo();

    std::cout << "\n--- 程序结束,局部对象将被自动销毁 ---\n";
    // 当 main 函数结束时,book1, book2, book3 会自动调用析构函数并被销毁

    return 0;
}



初始化列表

  • 初始化列表是构造函数的一部分,用于在构造函数体执行前初始化成员变量。
  • 必须使用初始化列表的场景:const 成员、引用成员、无默认构造函数的类成员。
  • 初始化顺序由类中声明的顺序决定,与初始化列表中的顺序无关

一个简单的示例

class MyClass{
private:
    int a;
    double b;
    std::string c;

public:
    //使用初始化列表来初始化字段
    Myclass(int x,double y,const std::string& z):a(x),b(y),c(z){
        //构造函数体
    }
};
#include <iostream>
using namespace std;

// 定义一个没有默认构造函数的类
class Helper {
public:
    Helper(int value) {
        cout << "Helper 构造函数: value = " << value << endl;
    }
};

class MyClass {
private:
    int normalVar;       // 普通成员变量
    const int constVar;  // const 成员变量
    int& refVar;         // 引用成员变量
    Helper helper;       // 没有默认构造函数的类成员

public:
    // 构造函数使用初始化列表
    MyClass(int a, int b, int c)
        : normalVar(a),    // 初始化普通变量
          constVar(b),    // 初始化 const 变量
          refVar(c),      // 初始化引用变量
          helper(c)       // 初始化 Helper 对象
    {
        cout << "MyClass 构造函数体" << endl;
    }

    void printValues() const {
        cout << "normalVar: " << normalVar << endl;
        cout << "constVar: " << constVar << endl;
        cout << "refVar: " << refVar << endl;
    }
};

int main() {
    int value = 42;
    MyClass obj(10, 20, value);  // 创建 MyClass 对象
    obj.printValues();           // 打印成员变量的值
    return 0;
}

 

this关键字

在C++中,this关键字是一个指向调用对象的指针。它在成员函数内部使用,用于引用调用该函数的对象,使用this可以明确指出成员函数正在操作的是哪个对象操作的数据成员。

#include <iostream>
#include <string>
using namespace std;

class Person {
private:
    string name;
    int age;

public:
    // 构造函数中使用 this 指针区分成员变量和参数
    Person(string name, int age) {
        this->name = name;
        this->age = age;
    }

    // 设置姓名的 setter 方法
    void setName(string name) {
        this->name = name;
    }

    // 设置年龄的 setter 方法
    void setAge(int age) {
        this->age = age;
    }

    // 返回当前对象的引用,支持链式调用
    Person& setInfo(string name, int age) {
        this->name = name;
        this->age = age;
        return *this;
    }

    // 打印信息的函数
    void printInfo() const {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    // 创建对象并调用函数
    Person p("Alice", 30);
    p.printInfo();  // 输出: Name: Alice, Age: 30

    // 使用链式调用设置信息
    p.setAge(31).setName("Bob").printInfo();  // 输出: Name: Bob, Age: 31

    // 另一个示例:使用 setInfo 进行链式设置
    Person p2("Charlie", 25);
    p2.setInfo("David", 26).printInfo();  // 输出: Name: David, Age: 26

    return 0;
}

注意这几点:

1.当构造函数的参数名与成员变量名相同时,必须使用 this-> 来明确指定是对对象成员的赋值。

2.链式调用(返回 *this):

return *this;

通过返回当前对象的引用,可以实现连续调用多个方法,如:

p.setAge(31).setName("Bob").printInfo();

3.printInfo 被声明为 const 方法: 表示该方法不会修改对象的状态,

 

用途示例
区分成员变量与参数this->name = name;
实现链式调用return *this;
传递当前对象someFunction(this);

new关键字

在C++中,new关键字用于动态分配内存。它是C++中处理动态内存分配的主要工具之一,允许程序运行时根据需要分配内存。

基本用法

分配单个对象:

使用new可以在堆上动态分配一个对象。例如,new int 会分配一个int类型的空间,并返回指向该空间的指针

int* p = new int;        // 分配一个 int 类型的内存空间
int* q = new int(10);    // 分配并初始化为 10

分配对象数组:

new 也可以用来分配一个对象数组。例如,new int[10]会分配一个包含10个整数的数组。

int* arr = new int[10];

分配类对象

MyClass* obj = new MyClass();  // 分配并调用构造函数

初始化:

可以在new表达式中使用初始化。用于单个对象,可以使用构造函数的参数。

MyClass* obj = new MyClass(arg1,arg2);

 

与delete配对使用

使用new分配的内存必须显式地通过delete(对于单个对象)或 delete[ ](对于数组)来释放,以避免内存泄露:

是释放单个对象

delete p;

 

释放数组

delete[ ] arr;

 

#include <iostream>
#include <new>  // 用于 std::nothrow

class MyClass {
public:
    MyClass() { std::cout << "MyClass created\n"; }
    ~MyClass() { std::cout << "MyClass destroyed\n"; }
    void print() { std::cout << "Hello from MyClass!\n"; }
};

int main() {
    // 1. 分配单个对象
    MyClass* obj1 = new MyClass();
    obj1->print();
    delete obj1;

    // 2. 分配数组
    MyClass* objArray = new MyClass[3];  // 调用 3 次构造函数
    delete[] objArray;                   // 调用 3 次析构函数

    // 3. 安全分配(nothrow)
    int* largeArray = new (std::nothrow) int[1000000000];
    if (largeArray) {
        std::cout << "Allocation succeeded!\n";
        delete[] largeArray;
    } else {
        std::cerr << "Allocation failed!\n";
    }

    // 4. 内存泄漏示例(注释掉 delete)
    int* leaky = new int(42);
    // delete leaky;  //  内存泄漏

    return 0;
}

 

十一、析构函数

析构函数是C++中的一个特殊的成员函数,他在4对象生命周期结束时被自动调用,用于执行对象销毁前的清理工作。其十分重要,尤其是在涉及动态分配的资源(如内存、文件句柄、网络连接等)的情况下。

  1. 名称:析构函数的名称由波浪号(~)后跟类名组成,如 ~Myclass()。
  2. 无返回值和参数:析构函数不接受任何参数,也不返回任何值。
  3. 自动调用:当对象的生命周期结束时(例如,一个局部对象的作用域结束,或者使用delete删除一个动态分配的对象),析构函数会被自动调用。
  4. 不可重载:每一个类只能有一个析构函数。
  5. 继承和多态:如果一个类是多态基类,其析构函数应该是虚的。
class 类名 {
public:
    ~类名(); // 声明析构函数
};

// 定义析构函数
类名::~类名() {
    // 清理逻辑
}

 

十二、静态成员

静态成员在C++类中是一个重要的概念,它包括静态成员变量和静态成员函数。

静态成员变量

  1. 定义:静态成员变量是类的所有对象共享的变量。与普通成员变量相比,无论创建了多少个类的实例,静态成员变量只有一份拷贝。
  2. 初始化:

 

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐