欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 《C++ 函数相关技术解析》

《C++ 函数相关技术解析》

2025/4/2 16:22:30 来源:https://blog.csdn.net/weixin_45136594/article/details/146885282  浏览:    关键词:《C++ 函数相关技术解析》

一、构造函数

1.1 定义与作用

        构造函数是一种特殊的成员函数,在创建对象时自动被调用,用于对对象进行初始化。其函数名与类名相同,没有返回值类型(包括 void 也不能有 )。

1.2 特性

  • 自动调用:每当创建类的对象时,构造函数都会被自动调用。
  • 默认构造函数:如果用户没有定义任何构造函数,编译器会自动生成一个默认构造函数。这个默认构造函数没有参数,函数体为空。但如果用户定义了至少一个构造函数,编译器就不会再自动生成默认构造函数。

1.3 调用形式

  • 无参构造函数调用Stu za; ,这种形式调用的是无参构造函数。
  • 有参构造函数调用
    • Stu za = Stu(参数); ,等价于 Stu za(参数); ,这两种形式都是调用带参数的构造函数。
    • Stu za = 参数; ,当参数类型匹配时,也会调用相应的带参构造函数。

1.4 初始化列表

        当类中有引用成员时,必须在构造函数的初始化列表中对其进行初始化,否则会导致编译错误。因为引用必须在定义时就被初始化,且之后不能再重新绑定到其他对象。

1.5 示例代码

#include <iostream>
class Stu {
private:int age;
public:// 无参构造函数Stu() {age = 0;std::cout << "无参构造函数被调用" << std::endl;}// 有参构造函数Stu(int a) {age = a;std::cout << "有参构造函数被调用,年龄为: " << age << std::endl;}
};
int main() {Stu stu1; // 调用无参构造函数Stu stu2(20); // 调用有参构造函数Stu stu3 = 22; // 调用有参构造函数return 0;
}

二、引用

2.1 在构造函数中的应用

        在构造函数的参数列表中使用引用类型,可以避免在函数调用时对传递的对象进行不必要的拷贝,从而提高程序的效率。特别是当传递的对象较大或者拷贝操作比较复杂时,这种方式的优势更加明显。

2.2 示例代码

#include <iostream>
class BigObject {// 假设这里有很多成员变量,使得对象占用较大空间int data[1000];
public:BigObject() {// 简单初始化for (int i = 0; i < 1000; ++i) {data[i] = i;}}
};
class Container {
private:BigObject obj;
public:// 使用引用传递参数,避免拷贝Container(const BigObject& bigObj) : obj(bigObj) {std::cout << "通过引用传递对象,避免了不必要的拷贝" << std::endl;}
};
int main() {BigObject big;Container container(big);return 0;
}

三、接口函数(set 和 get 接口 )

3.1 定义与作用

  • set 接口:专门用于给类的私有成员变量赋值,通常命名形式为 set + 成员变量名 。
  • get 接口:用于获取类的私有成员变量的值,通常命名形式为 get + 成员变量名 。

3.2 示例代码

#include <iostream>
#include <string>
class Person {
private:std::string name;int age;
public:// set 接口void setName(const std::string& n) {name = n;}void setAge(int a) {age = a;}// get 接口const std::string& getName() const {return name;}int getAge() const {return age;}
};
int main() {Person person;person.setName("Alice");person.setAge(25);std::cout << "姓名: " << person.getName() << ", 年龄: " << person.getAge() << std::endl;return 0;
}

四、this 指针

4.1 定义与作用

当对象调用成员函数时,this 指针会作为隐含参数传递给该函数,它指向调用该函数的对象本身。通过 this 指针,可以在成员函数中访问对象的成员变量和其他成员函数,并且可以区分成员变量和函数参数(当参数名与成员变量名相同时 )。

4.2 示例代码

#include <iostream>
class Point {
private:int x;int y;
public:Point(int x, int y) {// 使用 this 指针区分参数和成员变量this->x = x;this->y = y;}void print() {std::cout << "坐标: (" << x << ", " << y << ")" << std::endl;}
};
int main() {Point p(3, 4);p.print();return 0;
}

五、析构函数

5.1 定义与作用

        析构函数也是一种特殊的成员函数,与构造函数相反,它在对象生命周期结束时自动被调用,用于释放对象在生命周期内分配的资源(如动态分配的内存 ),执行一些清理工作。析构函数名是在类名前加上 ~ ,没有参数,也没有返回值类型。

5.2 特性

  • 自动调用:当对象超出作用域、使用 delete 释放对象(针对动态分配的对象 )等情况时,析构函数会自动被调用。
  • 默认析构函数:如果用户没有定义析构函数,编译器会自动生成一个默认析构函数。对于普通类,默认析构函数函数体为空;但对于包含动态分配资源或其他需要特殊清理操作的类,通常需要用户自定义析构函数。

5.3 示例代码

#include <iostream>
class Resource {
private:int* data;
public:Resource() {data = new int[10];std::cout << "构造函数被调用,分配内存" << std::endl;}~Resource() {delete[] data;std::cout << "析构函数被调用,释放内存" << std::endl;}
};
int main() {{Resource res;} // res 对象超出作用域,析构函数被自动调用return 0;
}

六、拷贝构造函数

6.1 定义与作用

        拷贝构造函数是一种特殊的构造函数,用于用一个已存在的对象来初始化同类型的新对象。其函数参数是本类对象的引用(通常为 const 类名& 形式 )。

6.2 调用场景

  • 函数参数传递:当函数的参数是对象值传递时,会调用拷贝构造函数创建一个实参对象的副本传递给函数。
  • 函数返回值:当函数返回一个对象时,如果返回方式是值返回,会调用拷贝构造函数创建一个临时对象作为返回值。

6.3 传参形式(const 类名 & )原因

  • 避免拷贝:使用引用传递参数,避免了在函数调用时对传递的对象进行额外的拷贝,提高了效率。
  • 防止修改const 修饰可以防止在拷贝构造函数内部意外修改传入的对象。

6.4 浅拷贝与深拷贝

  • 浅拷贝:默认的拷贝构造函数(即编译器自动生成的 )是浅拷贝。它只是简单地复制对象的成员变量的值,对于指针类型的成员变量,只是复制指针的值(即指向的地址 ),这样会导致新对象和原对象共享同一块动态分配的内存。当对象析构时,可能会出现多次释放同一块内存等问题。
  • 深拷贝:当类中包含指针类型的成员变量,且该指针指向动态分配的资源时,通常需要自定义拷贝构造函数来实现深拷贝。深拷贝会为新对象重新分配一块与原对象相同大小的内存,并将原对象中指针指向的内容复制到新分配的内存中,使得新对象和原对象拥有各自独立的资源副本。

6.5 示例代码

#include <iostream>
#include <cstring>
class String {
private:char* str;
public:String(const char* s) {str = new char[strlen(s) + 1];strcpy(str, s);std::cout << "构造函数被调用" << std::endl;}// 浅拷贝构造函数(错误示范)String(const String& other) {str = other.str;std::cout << "浅拷贝构造函数被调用(错误方式)" << std::endl;}// 深拷贝构造函数String(const String& other) {str = new char[strlen(other.str) + 1];strcpy(str, other.str);std::cout << "深拷贝构造函数被调用" << std::endl;}~String() {delete[] str;std::cout << "析构函数被调用" << std::endl;}
};
int main() {String s1("hello");String s2(s1);return 0;
}

七、static

7.1 静态存储区变量

  • 生命周期:静态存储区的变量在程序启动时分配内存,在程序结束时释放内存,其生命周期贯穿整个程序的运行过程。
  • 初始化:如果没有显式地对静态存储区变量进行初始化,那么对于数值类型的变量,会自动初始化为 0;对于指针类型的变量,会自动初始化为 NULL 。
  • 作用域:静态存储区变量在其定义的作用域内有效。如果是在函数内部定义的局部静态变量,它的作用域局限于该函数内部,但它的生命周期并不受函数调用结束的影响,在下次函数调用时,它的值会保持上一次函数调用结束时的值。

7.2 类的静态成员变量

  • 共享性:类的静态成员变量被该类的所有对象共享,无论创建了多少个该类的对象,静态成员变量在内存中只有一份拷贝。
  • 存储位置:静态成员变量存储在静态存储区,并不属于任何一个具体的对象实例。
  • 初始化:静态成员变量一般需要在类外进行初始化,格式为 数据类型 类名::静态成员变量名 = 初始值; 。
  • 访问方式:可以通过类名直接访问(类名::静态成员变量名 ),也可以通过对象访问(对象名.静态成员变量名 )。

7.3 类的静态成员函数

  • 无 this 指针:静态成员函数没有 this 指针,因为它并不属于任何一个具体的对象,所以不能直接访问类的非静态成员变量和非静态成员函数。
  • 访问权限:静态成员函数遵循类的访问控制规则,即如果是公有的静态成员函数,可以在类外通过类名或对象进行访问;如果是私有的静态成员函数,则只能在类内部访问。
  • 用途:常用于提供与类相关的工具函数,或者用于访问和操作类的静态成员变量。

7.4 示例代码

#include <iostream>
class Counter {
private:static int count;
public:Counter() {++count;}~Counter() {--count;}static int getCount() {return count;}
};
int Counter::count = 0;
int main() {Counter c1, c2;std::cout << "对象数量: " << Counter::getCount() << std::endl;return 0;
}

八、单例模式

8.1 饿汉模式

  • 原理:饿汉模式是在程序启动时就立即创建单例对象,由于是在单线程初始化阶段创建,所以不存在线程安全问题。
  • 实现方式:定义一个静态的单例对象,并在类外进行初始化。提供一个静态成员函数用于获取该单例对象。

8.2 懒汉模式

  • 原理:懒汉模式是在第一次使用单例对象时才进行创建,这种方式相对饿汉模式更加 “懒惰”,可以在一定程度上提高程序的启动效率。
  • 非线程安全实现:在获取单例对象的静态成员函数中,首先判断单例对象是否已经创建,如果没有创建则进行创建,然后返回单例对象。
  • 线程安全实现:为了在多线程环境下保证懒汉模式的正确性,需要使用锁机制(如 std::mutex )来防止多个线程同时创建单例对象。在获取单例对象的函数中,加锁后再进行对象是否已创建的判断和创建操作。

8.3 示例代码

#include <iostream>
#include <mutex>
// 饿汉模式
class SingletonEager {
private:static SingletonEager instance;SingletonEager() {}
public:static SingletonEager& getInstance() {return instance;}
};
SingletonEager SingletonEager::instance;
// 懒汉模式(非线程安全)
class SingletonLazy {
private:static SingletonLazy* instance;SingletonLazy() {}
public:static SingletonLazy* getInstance() {if (instance == nullptr) {instance = new SingletonLazy;}return instance;}
};
SingletonLazy* SingletonLazy::instance = nullptr;
// 懒汉模式(线程安全)
class SingletonThreadSafe {
private:static SingletonThreadSafe* instance;static std::mutex mutex_;SingletonThreadSafe() {}
public:static SingletonThreadSafe* getInstance() {std::lock_guard<std::mutex> guard(mutex_);if (instance == nullptr) {instance = new SingletonThreadSafe;}return instance;}
};
SingletonThreadSafe* SingletonThreadSafe::instance = nullptr;
std::mutex SingletonThreadSafe::mutex_;
int main() {SingletonEager& eager = SingletonEager::getInstance();SingletonLazy* lazy = SingletonLazy::getInstance();SingletonThreadSafe* threadSafe = SingletonThreadSafe::getInstance();return 0;
}

九、new 与 delete

9.1 new 的用法

  new 是 C++ 中用于动态内存分配的关键字,它在分配内存的同时会调用对象的构造函数进行初始化。

  • 分配单个对象
    • Stu* za = new Stu; :调用无参构造函数创建一个 Stu 类的对象,并返回指向该对象的指针。
    • Stu* za = new Stu(参数); :调用带参构造函数创建一个 Stu 类的对象,并返回指向该对象的指针。
  • 分配对象数组
    • Stu* arr = new Stu[10]; :创建一个包含 10 个 Stu 类对象的数组,调用无参构造函数初始化每个元素。
    • Stu* arr = new Stu[10](参数); :如果 Stu 类有合适的构造函数,会调用该构造函数初始化数组中的每个元素。
  • 分配基本数据类型变量和数组
    • int* a = new int; :创建一个未初始化的 int 类型变量。
    • int* a = new int(5); :创建一个初始化为 5 的 int 类型变量。
    • int* arr = new int[10]; :创建一个包含 10 个 int 类型元素的数组,元素未初始化。
    • int* arr = new int[10](); :创建一个包含 10 个 int 类型元素的数组,元素初始化为 0。

9.2 delete 的用法

delete 是与 new 对应的用于释放动态分配内存的关键字,它会调用对象的析构函数(如果是对象 )。

  • 释放单个对象Stu* za = new Stu; delete za; ,释放 za 指向的 Stu 类对象。
  • 释放对象数组Stu* arr = new Stu[10]; delete[] arr; ,释放 arr 指向的 Stu 类对象数组,会依次调用数组中每个对象的析

十、思维导图

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词