深拷贝和浅拷贝

浅拷贝就是将源对象的值拷贝给当前对象,两者指向的还是同一地址,对一个对象的修改可能会影响另一个对象。

class MyArray {
public:
    // 构造函数
    MyArray(int size) {
        this->size = size;
        data = new int[size];
    }
    // 拷贝构造函数,实现浅拷贝
    MyArray(const MyArray& other) {
        size = other.size;
        data = other.data; // 浅拷贝,将指针复制给新对象
    }
private:
    int size;
    int* data;
};

深拷贝是 一种对象拷贝,它会创建一个新的对象,并将原始对象中的所有数据成员复制到新的对象中,包括多态分配内存。原始对象和新的对象是完全独立的,对一个对象的修改不会影响另一个对象。

#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;
class Person {
    public:
        char* name; // 姓名
        int age;    // 年龄
        Person(const char* name, int age) {
            this->name = new char[strlen(name) + 1];
            strcpy(this->name, name);
            this->age = age;
        }
        // write your code here......
        Person(const Person& p)//拷贝构造函数
        {
            this->name = new char[strlen(p.name) + 1];
            strcpy(this->name, p.name);
            this->age = p.age;
        }
        void showPerson() {
            cout << name << " " << age << endl;
        }
        ~Person() {
            if (name != nullptr) {
                delete[] name;
                name = nullptr;
            }
        }
};

int main() {
    char name[100] = { 0 };
    int age;
    // 用户输入姓名和年龄
    cin >> name;
    cin >> age;
    // 
    Person p1(name, age);
   	// 实现浅拷贝
    Person p2 = p1;

    p2.showPerson();

    return 0;
}

区别:

  • 对象中含有指针类型的成员变量时需要用深拷贝构造则用浅拷贝构造
  • 编译器认的拷贝构造函数是浅拷贝构造函数
  • 如果对象中含有指针用了浅拷贝构造,那么会导致两个指针变量指向同一块地址空间,如果没有创建内存的操作就是浅拷贝,否则就是深拷贝
  • 深拷贝可以用于创建独立的副本,对于需要完全独立的对象的情况,必须在修改副本时不影响原始对象的状态。而浅拷贝通常用于创建共享对象,当需要多个共享相同的数据时,可以使用浅拷贝来减少内存占用和提高性能

友元函数

友元函数只是一个普通函数,并不是该类的类成员函数,它可以在任何地方调用,友元函数中通过对象名访问该类的私有或保护成员

friend <类型> <友元函数名>(<参数表>);
#include <iostream>
using namespace std; 
class A
{
public:
    A(int _a):a(_a){};
    friend int geta(A &ca);  //  < 友元函数
private:
    int a;
};
int geta(A &ca) 
{
    return ca.a;
}
int main()
{
    A a(3);    
    cout<<geta(a)<<endl;
    return 0;
}

友元类

friend class <友元类名>

类B是类A的友元,那么类B可以直接访问A的私有成员

  • 友元关系没有继承性
  • 友元关系没有传递性
#include <iostream>
using namespace std;
class A
{
public:
    A(int _a):a(_a){};
    friend class B;
private:
    int a;
};
class B
{
public:
    int getb(A ca) {
        return  ca.a; 
    };
};
int main() 
{
    A a(3);
    B b;
    cout<<b.getb(a)<<endl;
    return 0;
}

swap函数

交换函数

// 支持多种类型
swap(a,b); // 交换a和b的值,

switch连续

在使用switchcase时,遇到区间的情况,可以使用 ...来省略中间连续的数据。

#include <iostream>
using namespace std;
int main() {
    int month;
    cin >> month;
    // write your code here......
    if(month < 1 || month > 12)
        cout << "不合法" <<endl;
    else{
        switch (month) {
            case 3 ... 5:   //可以使用连续的数
                cout<<"春季"<<endl;
                break;
            case 6 ... 8:
                cout<<"夏季"<<endl; 
                break;
            case 9 ... 11:
                cout<<"秋季"<<endl;
                break;
            default:
                cout<<"冬季"<<endl;
        }
    }
    return 0;
}

输出

作用永久贯穿整个程序,直到格式状态改变设置为其他函数

// 设置输出格式
dec //置基数位10,相当于%d
hex // 置基数位16,相当于%x
oct // 置基数位8,相当于%o
cout << 12 << endl;//默认十进制
cout << dec << 12 << endl;//十进制
cout << hex << 12 << endl;//十六进制
cout << oct << 12 << endl;//八进制

// 设置保留一个小数 
cout << fixed << setprecision(1);

运行状态

就绪: 除了CPU资源,获得了所有必要资源,只要获取CPU,便可立即执行,进行此时状态为就绪 状态,当一个系统中处于就绪状态的进程可能有多个,排成一个队列 – 就绪队列

阻塞:正在执行的进程,由于等待某个事件(缺外部资源)发生 而无法执行,便放弃CPU而处于阻塞状态。

运行状态转换
运行状态

动态分区分配算法

在动态分区分配方式中,当很多个空闲分区 都能满足需求时,如何选择

  • 首次适应算法First Fit

每次都从地址开始查找,找到第一个能满足 大小的空闲分区

  • 最佳适应算法 Best Fit

为了保证大进程到来时能有连续得到大片空间。空闲分区按容量递增次序链接,每次分配内存时顺序查找空闲分区链(或空闲分区表),找到大小满足要求的第一个空闲分区。

  • 最坏(大)适应算法

每次分配优先使用最大的连续空闲区,这样分配后剩余空闲区不会太小,更方便使用,空闲分区按容量递减次序链接,每次分配内存时顺序查找空闲分区链或空闲分区表,找到大小能满足要求 的第一个空闲分区

  • 临近适应算法

空闲分区以地址递增的顺序排列(可排成一个循环链表),每次分配内存时从上次查找结束的位置开始查找空闲分区链(或空闲分区表),找到大小能满足要求第一个空闲分区。

网络层次

网络层次

网络层次

  • LLC逻辑链路层,主要负责站点间交换,差错控制,流量控制,应答功能

this指针

this指针类的指针,指向对象首地址.

this实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给this,this指针只能在成员函数中使用。在静态成员函数中不能用this

非静态成员都包含一个特殊的指针,指向调用该函数的对象,这个指针成为this指针

特点

  • 只能在成员函数中会用,全局静态函数友元函数没有this指针
  • this在成员函数的开始前构造,在成员函数的结束后清除
  • this指针不能被修改和赋值(它存放的是某一类对象地址)
  • this指针是一个隐含指针,是类成员函数的第一个默认参数,在函数体内可隐含使用它来访问本类的数据成员和成员函数,它并由编译器自动维护传递
  • this指针局部变量
  • this指针只有在对象调用成员函数时才被初始化重新定向,进入后不能再被修改
  • this并不是对象的一部分,不影响sizeof结果。

const区分重载

#include<iostream>
using namespace std;
class Person
{
public:
    void sum() const
    {
        int a = 1;
        int b = 2;
        cout << a + b << endl;
    };
    void sum()
    {
        int a = 2;
        int b = 4;
        cout << a + b << endl;
    };
};
int main()
{
    Person p;
    const Person q;
    p.sum();
    q.sum();
    return 0;
}

字符串相等问题

#include <iostream>
using namespace std;

int main()
{
    char str1[] = "hello world";
    char str2[] = "hello world";
    const char str3[] = "hello world";
    const char str4[] = "hello world";
    const char* pstring1 = "hello world";
    const char* pstring2 = "hello world";
    //数组不对比不相等
    cout << boolalpha << (str1 == str2) << ',';
    cout << boolalpha << (str3 == str4) << ',';   
    cout << boolalpha << (pstring1 == pstring2) << endl;  //相等

    return 0;
}

字符函数

字符判断函数 作用
isalpha() 判断字符是否是字母(‘a’-‘z’ ‘A’-‘Z’)
isdigit() 判断字符是否是数字
isspace() 判断字符是否是空格制表符换行等标准空白
isalnum() 判断字符是否是字母或者数字
ispunct() 判断字符是标点符号
islower() 判断字符是否是小写字母(‘a’-‘z’)
isupper() 判断字符是否是大写字母(‘A’-‘Z’)
// 使用字符函数统计字符串中各类型字符的个数
#include <cctype>
#include <iostream>
#include <string>
using namespace std;
int main() {
    string str;
    getline(cin, str);
    int whitespace = 0;
    int digits = 0;
    int chars = 0;
    int others = 0;

    // write your code here......
    for(int i = 0;i<str.size();i++)
    {
        if(isalpha(str[i]))
        {
            chars++;
        }
        else if(isdigit(str[i]))
        {
            digits++;
        }
        else if(isspace(str[i]))
        {
            whitespace++;
        }
        else {
            others++;
        }
    }
    cout << "chars : " << chars
        << " whitespace : " << whitespace
        << " digits : " << digits
        << " others : " << others << endl;

    return 0;
}

反转链表的实现

反转链表

给定一个单链表的头结点pHead该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0≤10000≤n≤1000
要求:空间复杂度 O(1) ,时间复杂度 O*(n) 。

正解迭代

反转链表迭代法
反转链表迭代法

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param head ListNode类 
     * @return ListNode类
     */
    ListNode* ReverseList(ListNode* head) {
        // write code here
        ListNode* pre = nullptr; //记录前一个节点,并作为链表反向指针
        ListNode* cur = head;  // 当前节点指针
        ListNode* nex = nullptr; //指向下一个节点指针
        while(cur!=nullptr)
        {
            //为nex节点指向指针
            nex = cur->next;
            // 将当前节点的next指针转向
            cur->next = pre;
            // 前驱节点后移
            pre = cur;
            // 当前节点同时也后移
            cur = nex; //当前节点后移
        }
        return pre;
    }
};
  • 获取前最大的k个值,使用最大堆,最小的话,使用最小堆。