侯捷C++学习记录(截至参数传递与返回值)

侯捷C++学习:参数传递与返回值要点

1.inline(内联)函数

函数若在class body内定义完成,则自动成为内联函数;但是是否一定会是inline, 还得看编译器。

若没有在本体内定义,但是想要成为内联函数,需要在函数声明前加上inline。
注意: operator 是 C++ 中的关键字。主要用途是重载(overload)运算符。通过使用 operator 关键字,程序员可以为自定义类型(如类或结构体)定义或修改标准运算符(如 +, -, *, /, ==, [], (), ++ 等)的行为。
基本用法:

返回类型 operator 运算符符号 (参数列表) {
    // 函数体,定义运算符的行为
}

2.构造函数

C++中创建一个对象,肯定会自动调用构造函数,就是靠构造函数来创建对象的

强烈建议使用列表初始化来赋初始值,效率要比在大括号内快,因为在大括号等同于是先声名,再赋值进行初始化,而初始化列表等于一步到位。若在大括号进行,等于放弃了一步到位的方式。
注意:class 有两种,带指针与不带指针的,上面案例则为不带指针的,多半不带指针的类不用析构函数。

3.重载

在这里插入图片描述
上面案例中,real函数为重载的,一个用来设置,一个用来取值,编译器编译的实际名称其实是不一样的,使用的时候根据他们传不传参,进行区分。
注意:上面案例中的两个构造函数是一样的,所以不属于重载,不允许这样写。 -----函数重载常发生在构造函数

4.构造函数可以私有化,单例模式singleton就是私有化

在这里插入图片描述

5.常量成员函数const member function

  • class 中的函数分为会改变数据的和不会改变数据的,对于不会改变数据的建议在函数体前加上const即常量成员函数;根据函数这种情况,在类中设计接口的时候就知道是否需要加上const,
  • const可能出现在对象或者变量的前面,则表示此对象或变量是不变的,若用此对象去调用没有加const的函数则会出问题,也就是说声明为const的对象必须调常量成员函数,但是常量成员函数可以被带不带const的对象均可调用

6.函数传递最好是传引用,可以避免拷贝大型对象,提高效率,所以尽量不要传value,不要纠结传字符只有一个字节比引用的四个字节小,按大局为重,就传引用。

  • 注意:因为传引用相当于传指针,若函数修改了引用里的值,肯定会修改原始值,为了防止修改需要在传入参数的函数的参数声明前添加const,即引用类型声明之前:
    void func(const Type& paramName);
    示例:
  • 不加 const —— 允许修改原始值
#include <iostream>
using namespace std;

void increment(int& x) {
    x++; // 修改了引用所指向的值
}

int main() {
    int a = 5;
    cout << "Before: " << a << endl; // 输出 5
    increment(a);
    cout << "After: " << a << endl;  // 输出 6
    return 0;
}

函数修改了原始变量 a 的值。

  • 加上 const —— 禁止修改原始值
#include <iostream>
using namespace std;

void printValue(const int& x) {
    // x++; // ❌ 编译错误!不能修改 const 引用
    cout << "Value: " << x << endl;
}

int main() {
    int a = 10;
    printValue(a); // 安全地传递,不会被修改
    cout << "a is still: " << a << endl; // a 仍然是 10
    return 0;
}

使用 const int& 可以防止函数内部修改 a,同时避免了拷贝。

  • 大型对象传 const 引用(推荐做法)
#include <iostream>
#include <string>
using namespace std;

void printString(const string& str) {
    // str += "abc"; // ❌ 错误:不能修改 const 引用
    cout << str << endl;
}

int main() {
    string s = "Hello, World!";
    printString(s); // 高效且安全,没有拷贝 string 对象
    return 0;
}

C++ 中处理大型对象(如 string, vector, 自定义类等)的最佳实践:使用 const T& 避免拷贝并防止修改。只要你不打算修改参数,就用 const Type&,这是 C++ 的良好习惯。
在这里插入图片描述

引用主要用在两方面:函数参数传递,函数返回值传递

在这里插入图片描述

7.友元(friend)

友元可以直接获取其类中的私有成员
在这里插入图片描述

  • 同一个 class 的不同 objects 可以互相访问彼此的 private 成员,这是 class 的访问控制特性决定的,不是因为它们是友元。
  • 在 C++ 中,访问控制(access control)是以 class 为单位,而不是以 object 为单位。
    也就是说:
    private 成员只能被 该 class 的成员函数 访问。
    不管这个成员函数是被哪个 object 调用的,也不管它访问的是哪个 object 的 private 成员。
    只要是在类的成员函数内部,就可以访问同类任意对象的 private 成员。
#include <iostream>
using namespace std;

class Person {
private:
    int secret;

public:
    Person(int s) : secret(s) {}

    // 成员函数:比较 this 和 another 的 secret
    bool isSameSecret(const Person& another) const {
        return secret == another.secret;  // ✅ 合法!访问 another 的 private 成员
    }
};

int main() {
    Person p1(42);
    Person p2(42);
    Person p3(100);

    cout << p1.isSameSecret(p2) << endl; // 输出 1 (true)
    cout << p1.isSameSecret(p3) << endl; // 输出 0 (false)

    return 0;
}

📌 关键点:isSameSecret 函数中访问了 another.secret,而 secret 是 private 的,但这是合法的!

  • “类是封装的单元,而不是对象是封装的单元。”
    类的设计者完全掌控类的内部逻辑。
    如果类的成员函数需要比较、复制、操作同类的其他对象(如赋值运算符、拷贝构造函数等),必须能访问其 private 成员。
    否则,很多基本操作(如深拷贝、比较、赋值)将无法实现。
    示例,拷贝构造函数:
Person(const Person& other) : secret(other.secret) { } // 必须能访问 other 的 private
  • “友元”(friend)是一个显式的、需要声明的机制,用于打破封装,让其他类或函数访问 private 成员。
    比如:
class A {
private:
    int data;
    friend class B; // 显式声明 B 是友元
};

class B {
public:
    void accessA(A& a) {
        a.data = 100; // ✅ 因为 B 是 A 的 friend
    }
};

class设计一般要求的结构:构造函数使用列表初始值;数据在private中;传参尽可能使用引用传递,对于const的添加看具体情况;返回值也尽量使用引用传递;对于类中该加const的必须加,防止调用出错。

对于临时用于存储计算结果的变量,不可以返回值使用引用,因为函数结束后临时变量就没了。在这里插入图片描述

如图中第一个函数,是对象1和2中的内容相加再赋值到1,1本来就存在,所以可以传他的引用。

任何成员函数都有一个隐藏的this指针。

哪个对象调用此函数,此函数的this指针则指向此对象。

下图第一个函数表示返回的值为value,接收端接收的是一个引用,合法;

在这里插入图片描述
操作符重载在这里插入图片描述
下面代码中不能使用返回引用,是因为,函数计算出的值放在临时的值,而不是之前已存在的变量值。
在这里插入图片描述
此处typename ();表示创建临时对象,complex ();就是创建complex的临时对象;int ();就是创建int的临时对象,因为是临时需要的,函数结束就没了,所以也就没有对象名称。就好像临时创建一个空间用来存放计算的结果来立马返回结果。虽然平时写代码较少用到这种方式,但临时对象在标准库中用的较多。
下面两个没有名称的也是临时对象,第一个没有参数即为构造函数的默认参数,进行到(4,5)的下一行生命即终止:
在这里插入图片描述
正负号运算:
在这里插入图片描述
根据参数的个数来确定是“正负运算”还是“加减运算”。
上面负号运算会产生一个新的东西,所以需要临时创建空间(临时对象)来保存新东西,所以返回的不能是引用,应该是值。
而对于正号运算并没有产生新东西,但此处依然是传值,而不是引用。老师说标准库就是这样写的,我们也可以试试传引用,很有可能也是没问题的(可以试试看)。
在这里插入图片描述
在这里插入图片描述
上图中os即是cout;之所以返回的是ostream&是为了满足后面的连续输出,若只满足前面只输出一个conj(c1),其实返回的可以是void。如下图:
在这里插入图片描述
在这里插入图片描述

### 关于侯捷 C++ 系列课程学习笔记 #### 内存管理和 `std::allocator` 在探讨内存管理时,`std::allocator` 是一个重要的概念。它提供了一种通用的方式来进行动态内存分配和释放操作[^1]。通过使用 `std::allocator`,可以更灵活地控制容器内部对象的创建销毁。 ```cpp #include <memory> using namespace std; int main() { allocator<int> alloc; const int n = 1000; int* p = alloc.allocate(n); // 构造元素 uninitialized_fill_n(p, n, 0); // 销毁并回收内存 destroy_n(p, n); alloc.deallocate(p, n); } ``` 这段代码展示了如何利用 `std::allocator` 来手动管理一块整型数据的空间,并对其进行初始化以及最终清理工作。 #### 使用迭代器简化输入处理 对于从标准输入流读取数值并将这些值存储至目标容器的操作,可以通过组合 `istream_iterator` 和算法库中的 `copy()` 函数来实现简洁高效的解决方案[^2]: ```cpp #include <iostream> #include <iterator> #include <vector> #include <algorithm> using namespace std; int main(){ vector<double> c; istream_iterator<double> eos; // 表示结束标记 istream_iterator<double> iit(cin); // 创建基于cin的输入迭代器 copy(iit, eos, back_inserter(c)); // 将所有输入复制到向量c中 } ``` 此程序片段能够方便地接收来自用户的多个浮点数作为输入,并自动将其追加到指定的目标集合内而无需显式循环结构。 #### 移动语义的重要性及其应用 当涉及到像 `std::vector` 这样的序列式容器时,确保自定义类型的移动构造函数和赋值运算符被声明为 `noexcept` 至关重要。这不仅有助于提高性能,还可能影响某些 STL 容器的行为逻辑,比如扩容机制会优先考虑调用 noexcept 的版本以减少异常风险[^3]。 ```cpp class MyClass { public: MyClass(MyClass&& other) noexcept : member(std::move(other.member)) {} MyClass& operator=(MyClass&& other) noexcept { if (this != &other){ member = std::move(other.member); } return *this; } private: string member; }; ``` 上述类实现了无抛出保证的右值引用重载方法,从而使得该类型更适合用于频繁变动大小的数据结构之中。 #### 初始化列表 (`initializer_list`) 及其特性 最后,在介绍现代 C++ 特性的过程中不得不提到 `initializer_list`。这是一种特殊的模板参数形式,允许我们采用花括号语法传递一组同质化的初始值给构造函数或其他接受此类参数的地方[^4]。值得注意的是,尽管看起来像是拥有自己的副本,实际上 initializer_list 并不持有任何实际的对象实例——它仅仅是指向原始数组的一个视图而已。 ```cpp template<typename T> void print(const initializer_list<T>& ilist){ for(auto elem : ilist) cout << elem << " "; } // 调用方式如下所示: print({1, 2, 3}); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值