椋鸟C++笔记#3:类的默认成员函数

【笔记软件obsidian】从入门到精通 目录 一、前言 二、下载及安装 三、【选修】ios用户如何通过iCloud进行三平台同步 四、基本Markdown语法 五、【选修】插件的下载及安装方式 六、 阅读详情

萌新的学习笔记,写错了恳请斧正。

类的默认成员函数

我们写一个类,在类体中什么都不写,这样的类就叫空类。

空类真的什么都没有吗?并不是的。

类有六个默认成员函数,如果我们自己没有写这六个函数,类也会自己去补齐它们。

  1. 默认构造函数(Default Constructor) - 用于无参的初始化一个对象。
  2. 析构函数(Destructor) - 用于在对象生命周期结束时进行资源的回收等收尾工作。
  3. 拷贝构造函数(Copy Constructor) - 用于通过同类型的另一个对象来初始化新对象。
  4. 拷贝赋值运算符(Copy Assignment Operator) - 用于将一个对象的内容赋值给另一个同类型的对象。
  5. 移动构造函数(Move Constructor) - C++11引入,用于通过"移动"而非拷贝另一个同类型的对象来初始化新对象。
  6. 移动赋值运算符(Move Assignment Operator) - C++11引入,用于通过"移动"而非拷贝来将一个对象的内容赋值给另一个同类型的对象。

其中后两个先不管,下面我们介绍一下前面几个函数。

构造函数(Constructor)

我们先看这样一个类:

class Date
{
public:
    void Init(int y, int m, int d)
    {
        m_year = y;
        m_month = m;
        m_day = d;
    }
    
    void Print()
    {
        cout << m_year << "-" << m_month << "-" << m_day << endl;
    }
    
private:
    int m_year;
    int m_month;
    int m_day;
}

对于这个日期类,我们每次要实例化一个对象时都需要这样先创建对象再使用Init函数完成初始化,非常的不方便。

所以在C++中,我们就有了构造函数。构造函数是一个特殊的成员函数,名字与类名相同。当我们创建对象时,其构造函数就会被自动调用,并且在对象的整个生命周期中只调用一次。

比方说我们可以把上面的类改写成这样:

class Date
{
public:
    Date(int y, int m, int d)	// 构造函数前面就不要加void了
    {
        m_year = y;
        m_month = m;
        m_day = d;
    }
    
    void Print()
    {
        cout << m_year << "-" << m_month << "-" << m_day << endl;
    }
    
private:
    int m_year;
    int m_month;
    int m_day;
}
构造函数的特点
  1. 函数名与类名相同。

  2. 没有返回值。

  3. 对象实例化时编译器自动调用对应的构造函数。

  4. 同样可以重载:

    class Date
    {
    public:
        Date()	// 无参
        {
            m_year = 1900;
            m_month = 1;
            m_day = 1;
        }
    
        Date(int y, int m, int d)	// 带参
        {
            m_year = y;
            m_month = m;
            m_day = d;
        }
    
        void Print()
        {
            cout << m_year << "-" << m_month << "-" << m_day << endl;
        }
    
    private:
        int m_year;
        int m_month;
        int m_day;
    }
    
    int main()
    {
        Date d1;	// 调用无参的构造函数
        Date d2(2024, 5, 22);	//调用带参的构造函数
    
        return 0;
    }
    

    注意:这里实例化时不能这样写:

    int main()
    {
        Date d();	// 调用无参的构造函数不要带括号!
    
        return 0;
    }
    

    这里的语句可能会被认为是在声明一个叫d的函数,其返回值是Date类型的对象。

  5. 如果类中没有显式定义构造函数,编译器会自动生成一个无参的构造函数,如果显式定义了就不会这么做。

    这里默认生成的构造函数是遵循什么规则给对象初始化的呢?且看:

    C++中的类型分为内置类型(基本类型,比如int、double这些自带的)和自定义类型(我们用class、struct、union等自己定义出来的)。而默认生成的构造函数不一定会对内置类型进行初始化(不排除某些编译器有自己的想法?),对于自定义类型编译器会去寻找其构造函数以实现初始化。

    但是在C++11中,打了一个补丁:内置成员变量可以在类中声明时给默认值(要给就全给)。

    class Date
    {
    public:
        Date(int y, int m, int d)
        {
            m_year = y;
            m_month = m;
            m_day = d;
        }
    
        void Print()
        {
            cout << m_year << "-" << m_month << "-" << m_day << endl;
        }
    
    private:
        int m_year = 1900;
        int m_month = 1;
        int m_day = 1;
    }
    
    int main()
    {
        Date d;	//1900-1-1
        d.Print();
    
        return 0;
    }
    

析构函数 (Destructor)

析构函数与构造函数相反,在对象销毁时自动调用析构函数,完成对象中资源的清理工作。

析构函数的特点
  1. 析构函数名是在类名前加上字符~。
  2. 析构函数无参数无返回值类型,不能重载。
  3. 一个类只能有一个析构函数。若显式未定义,则自动生成默认的析构函数。
  4. 对象生命周期结束时,自动调用析构函数。
  5. 和构造函数同样的,自动生成的析构函数不会清理内置类型的变量(本身也不需要清理),对于自定义类型的变量去寻找其析构函数并调用。

这里我们还是以之前的Stack类为例:

#pragma once

#include <iostream>
#include <string>

using namespace std;

typedef int StackDataType;

class Stack
{
public:
	Stack(int size);
	~Stack();
	void Push(StackDataType data);
	StackDataType Pop();
	StackDataType Top();
	bool IsEmpty();
	bool IsFull();
	int GetSize();
	int GetTop();

private:
	StackDataType* m_data;
	int m_size;
	int m_capacity;
};
#include "Stack.h"

Stack::Stack(int size = 4)
{
	m_data = new StackDataType[size];
	m_size = 0;
	m_capacity = size;
}

Stack::~Stack()
{
	delete[] m_data;
}

void Stack::Push(StackDataType data)
{
	if (IsFull())
	{
		cout << "Stack is full!" << endl;
		return;
	}

	m_data[m_size++] = data;
}

StackDataType Stack::Pop()
{
	if (IsEmpty())
	{
		cout << "Stack is empty!" << endl;
		return -1;
	}

	return m_data[--m_size];
}

StackDataType Stack::Top()
{
	if (IsEmpty())
	{
		cout << "Stack is empty!" << endl;
		return -1;
	}

	return m_data[m_size - 1];
}

bool Stack::IsEmpty()
{
	return m_size == 0;
}

bool Stack::IsFull()
{
	return m_size == m_capacity;
}

int Stack::GetSize()
{
	return m_size;
}

int Stack::GetTop()
{
	return m_size - 1;
}

拷贝构造函数(Copy Constructor)

如果我们想通过一个已有对象去创建一个新对象,就要用到拷贝构造函数。简单来说,就是我们有一个类X的对象a,现在想再创建一个类X的对象b而且要和a完全一致。那么就直接使用拷贝构造函数,使用a来创建b。

拷贝构造函数的特点
  1. 拷贝构造函数和默认构造函数一样都是构造函数的一个重载形式。

  2. 拷贝构造函数的参数只有一个且为类类型对象的引用。使用传值的方式会造成无穷递归调用,会被编译器报错。

    class Date
    {
    public:
        Date(int y, int m, int d)
        {
            m_year = y;
            m_month = m;
            m_day = d;
        }
        
        Date(const Date& d)
        {
            m_y = d.m_y;
            m_m = d.m_m;
            m_d = d.m_d;
        }
    
        void Print()
        {
            cout << m_year << "-" << m_month << "-" << m_day << endl;
        }
    
    private:
        int m_year = 1900;
        int m_month = 1;
        int m_day = 1;
    }
    

    与之类似的,我们一般传参时,能使用引用就尽量使用引用类型,可以提高程序效率。

  3. 如果没有显式定义,编译器会生成默认的拷贝构造函数。但是默认的拷贝构造函数是直接将内存按字节序复制过来,属于浅拷贝(值拷贝)。但是我们很多时候是需要深拷贝的,尤其是存在资源申请时,因为这种情况下依旧直接复制内存并不能申请新的资源,不能完成我们所需的拷贝。

运算符重载(Operator Overloading)

运算符重载是面对对象编程的一种方法。在C++中,运算符重载允许我们为类对象自定义运算符的计算方法。

简单来说,我们自定义一个类,编译器是不知道这个类的加减乘除等计算是如何进行的。像加减乘除等计算,一般只能用于系统的内置类型,但我们可以通过运算符重载来规定自定义类型遇到这些运算符应该怎么处理。

运算符重载函数的名字为:operator后面加上需要重载的运算符符号。

比方说下面就是重载了日期类的日期加天数等于新日期的计算(+=的重载):

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
    {
        m_year = year;
        m_month = month;
        m_day = day;
	}
    
	Date(const Date& date)
    {
        m_year = date.m_year;
        m_month = date.m_month;
        m_day = date.m_day;
    }
        
	~Date() {};

	int GetMonthDays(int year, int month)
    {
        if (month == 2)
        {
            if (IsLeapYear(year))
            {
                return 29;
            }
            else
            {
                return 28;
            }
        }
        else if (month == 4 || month == 6 || month == 9 || month == 11)
        {
            return 30;
        }
        else
        {
            return 31;
        }
    }
    
	bool IsLeapYear(int year)
    {
        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

	Date& operator+=(int days)
    {
        while (days > 0)
        {
            if (days + m_day > GetMonthDays(m_year, m_month))
            {
                days -= GetMonthDays(m_year, m_month) - m_day + 1;
                m_day = 1;
                if (m_month == 12)
                {
                    m_month = 1;
                    m_year++;
                }
                else
                {
                    m_month++;
                }
            }
            else
            {
                m_day += days;
                days = 0;
            }
        }
        return *this;
    }

	void Print()
    {
        std::cout << m_year << "-" << m_month << "-" << m_day << std::endl;
    }

private:
	int m_year;
	int m_month;
	int m_day;
};

我们也可以将运算符重载为全局函数。但是需要涉及的成员变量是公有的(这会破坏类的封装性,但是也可以通过友元来解决,后面会讲):

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
    {
        m_year = year;
        m_month = month;
        m_day = day;
	}
    
	Date(const Date& date)
    {
        m_year = date.m_year;
        m_month = date.m_month;
        m_day = date.m_day;
    }
        
	~Date() {};

	int m_year;
	int m_month;
	int m_day;
};

bool operator==(const Date& d1, const Date& d2)
{
    return d1.m_year == d2.m_year
        && d1.m_month == d2.m_month
        && d1.m_day == d2.m_day;
}

void test()
{
    Date d1(2024, 1, 1);
    Date d2(2024, 1, 1);
    cout << (d1 == d2) << endl;	// 由于优先级问题需要加括号
}

注意:

  1. 只能重载已有的运算符,不能自创新的符号。
  2. 不能对内置类型去重载。
  3. 有5个运算符不能重载:.*(通过成员函数指针访问类的成员函数的运算符)、::(作用域限定符)、sizeof、?:(三目运算符)、.
  4. 重载操作符至少要有一个类类型参数。作为类成员函数重载时,其有一个隐藏的参数,为其第一个参数this。
赋值运算符重载

赋值运算符重载与拷贝构造函数类似。其参数应当选择传引用返引用以提高效率,并且这样能够支持连续赋值。

注意:

  1. 应当注意是否出现自己给自己赋值的情况。
  2. 赋值运算符不能重载为全局函数,只能是成员函数(全局函数没有this指针,而且赋值运算符如果不显式实现,编译器会生成一个默认的,此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突)。
  3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式在内存逐字节拷贝(浅拷贝)。

我们还是以日期类为例:

Date& Date::operator=(const Date& date)
{
	if (this != &date)
	{
		m_year = date.m_year;
		m_month = date.m_month;
		m_day = date.m_day;
	}
	return *this;
}
前置/后置的自增和自减运算符重载

我们以自增运算符为例,前置自增应当这样重载:

Date& Date::operator++()
{
	*this += 1;
	return *this;
}

而后置自增运算符应当如何重载呢?C++规定:为了区分,后置运算符重载时增加一个int类型参数,但是调用该函数时忽略该参数,就像这样:

Date Date::operator++(int)
{
	Date date = *this;
	*this += 1;
	return date;
}

void test()
{
    Date d1;
    Date d2(2024, 1, 1);
    d1 = d2++;
    d1 = ++d2;
}

注意后置自增是使用后再自增,因此要返回自增前的旧值。

const成员

在成员函数的括号后面加一个const,就将这个成员函数称为const成员函数。这里实际修饰的是该成员函数的this指针,表示这个成员函数不能对类的任何成员进行修改。

建议:只要能加const就加const,避免权限放大的问题。

具体的例子可以看下面实现完整的日期类中。

实现完整的日期类

下面我们通过上面学的内容实现一个日期类吧!

#pragma once

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1);
	Date(const Date& date);
	~Date();

	int GetMonthDays(int year, int month) const;
	int GetYearDays(int year) const;
	bool IsLeapYear(int year) const;

	Date& operator+(int days) const;
	Date& operator-(int days) const;
	Date& operator-=(int days);
	Date& operator+=(int days);
	Date& operator++();
	Date operator++(int);
	Date& operator--();
	Date operator--(int);
	Date& operator=(const Date& date);
	bool operator==(const Date& date) const;
	bool operator!=(const Date& date) const;
	bool operator<(const Date& date) const;
	bool operator>(const Date& date) const;
	bool operator<=(const Date& date) const;
	bool operator>=(const Date& date) const;
	int operator-(const Date& date) const;

	void Print() const;

private:
	int m_year;
	int m_month;
	int m_day;
};
#include <iostream>
#include "Date.h"

using namespace std;

Date::Date(int year, int month, int day)
{
	m_year = year;
	m_month = month;
	m_day = day;
}

Date::Date(const Date& date)
{
	m_year = date.m_year;
	m_month = date.m_month;
	m_day = date.m_day;
}

Date::~Date()
{
}

int Date::GetMonthDays(int year, int month) const
{
	if (month == 2)
	{
		if (IsLeapYear(year))
		{
			return 29;
		}
		else
		{
			return 28;
		}
	}
	else if (month == 4 || month == 6 || month == 9 || month == 11)
	{
		return 30;
	}
	else
	{
		return 31;
	}
}

int Date::GetYearDays(int year) const
{
	if (IsLeapYear(year))
	{
		return 366;
	}
	else
	{
		return 365;
	}
}

bool Date::IsLeapYear(int year) const
{
	if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Date& Date::operator+(int days) const
{
	Date date = *this;
	date += days;
	return date;
}

Date& Date::operator-(int days) const
{
	Date date = *this;
	date -= days;
	return date;
}

Date& Date::operator-=(int days)
{
	while (days > 0)
	{
		if (days >= m_day)
		{
			days -= m_day;
			if (m_month == 1)
			{
				m_month = 12;
				m_year--;
			}
			else
			{
				m_month--;
			}
			m_day = GetMonthDays(m_year, m_month);
		}
		else
		{
			m_day -= days;
			days = 0;
		}
	}
	return *this;
}

Date& Date::operator+=(int days)
{
	while (days > 0)
	{
		if (days + m_day > GetMonthDays(m_year, m_month))
		{
			days -= GetMonthDays(m_year, m_month) - m_day + 1;
			m_day = 1;
			if (m_month == 12)
			{
				m_month = 1;
				m_year++;
			}
			else
			{
				m_month++;
			}
		}
		else
		{
			m_day += days;
			days = 0;
		}
	}
	return *this;
}

Date& Date::operator++()
{
	*this += 1;
	return *this;
}

Date Date::operator++(int)
{
	Date date = *this;
	*this += 1;
	return date;
}

Date& Date::operator--()
{
	*this -= 1;
	return *this;
}

Date Date::operator--(int)
{
	Date date = *this;
	*this -= 1;
	return date;
}

Date& Date::operator=(const Date& date)
{
	if (this != &date)
	{
		m_year = date.m_year;
		m_month = date.m_month;
		m_day = date.m_day;
	}
	return *this;
}

bool Date::operator==(const Date& date) const
{
	if (m_year == date.m_year && m_month == date.m_month && m_day == date.m_day)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool Date::operator!=(const Date& date) const
{
	return !(*this == date);
}

bool Date::operator<(const Date& date) const
{
	if (m_year < date.m_year)
	{
		return true;
	}
	else if (m_year == date.m_year)
	{
		if (m_month < date.m_month)
		{
			return true;
		}
		else if (m_month == date.m_month)
		{
			if (m_day < date.m_day)
			{
				return true;
			}
		}
	}
	return false;
}

bool Date::operator>(const Date& date) const
{
	return !(*this < date) && *this != date;
}

bool Date::operator<=(const Date& date) const
{
	return *this < date || *this == date;
}

bool Date::operator>=(const Date& date) const
{
	return *this > date || *this == date;
}

int Date::operator-(const Date& date) const
{
	int days = 0;
	Date temp = *this;
	if (temp < date)
	{
		while (temp != date)
		{
			temp++;
			days++;
		}
	}
	else
	{
		while (temp != date)
		{
			temp--;
			days++;
		}
	}
	return days;
}

void Date::Print() const
{
	std::cout << m_year << "-" << m_month << "-" << m_day << std::endl;
}

关于取地址和const取地址运算符(&操作符)

有人说我们自定义的类型能够直接取地址或者const取地址,因此这两种运算符也是被编译器默认重载了的默认成员函数。

事实上这不能认为是类的默认成员函数,虽然我们自己可以去重载这两个运算符(不推荐,一般也用不上),但是空类生成的时候编译器并没有生成这样一个重载成员函数,这个操作是C++内置的基础功能。只有当显式地为类重载了取地址操作符,这个操作的行为才会改变。

数学建模之0—1规划(Python) 目录 1、概述 (1)知识框架 (2)线性整数规划模型 (3)分支定界法 2、案例 (1)案例1​ (2)案例2 3、遗传算法—sko-GA包实现 (1)代码 (2)结果 4、PuLp实现 (1)代码 (2)结果 5、致谢 1、概述 (1)知识框架 (2)线性整数规划模型 (3)分支定界法 2、案例 (1)案例1 (2)案例2 3、遗传算法—sko-GA包... 阅读详情

相关推荐

二手分析仪器改造实战:如何用PMT搭建荧光检测电路(附完整电路图)

本文详细介绍了如何利用二手分析仪器中的光电倍增管(PMT)核心部件,进行荧光检测电路的实战改造。内容涵盖从PMT工作原理理解、高压供电电路设计、低噪声前置放大(跨阻放大器)到信号调理的完整流程,并提供了关键电路图与调试排错指南,旨在帮助电子爱好者与工程师低成本构建稳定、高灵敏度的专业检测系统。

weixin_29234239的博客 441

椋鸟C++笔记#2:从结构体到类

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 1237

关于DHCP Option60的认识

<br />       最近在网络建设过程中,必须面对一个实际存在的问题:当只有一根线入户时,该如何解决CATV和宽带业务的共存呢?这时就要考虑用到DHCP Option60了,那么DHCP Option60到底是如何来实现业务区分的呢?<br />首先先来看下DHCP协议,在DHCP协议中,定义了一个option字段,该字段主要是用来扩展DHCP协议的,Option 60 (Vendor class identifier)是DHCP报文中的一个选项字段,code为60,可以标识终端类型,根据不同的终

luoyingchuan的专栏 7940

椋鸟C++笔记#1:C++初识

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 916

海外支付实战:虚拟信用卡订阅ChatGPT Plus等服务的完整指南

在跨境数字服务消费中,支付环节常因地区限制、银行风控等因素受阻。其核心原理在于国际信用卡交易需通过发卡行风控系统,而国内银行对高频、小额或商户名非常规的境外线上交易敏感,导致成功率不稳定。为解决这一痛点,虚拟信用卡技术应运而生,它通过服务商生成关联预充值账户的虚拟卡号,提供地域灵活性与资金可控性,显著提升了支付通过率。这项技术的价值在于以较低学习成本,为个人用户打通了访问ChatGPT Plus、Midjourney、Claude Pro等前沿AI服务的支付通道。其典型应用场景包括订阅各类海外SaaS、内容

weixin_42531579的博客 1028

k8s配置habor作为镜像地址之后,如果harbor中没有镜像,如何设置自动从阿里云官方镜像仓库下载

通过 Harbor 的远端仓库可以集中管理。如果你在上游设置了 image prefix 为 library,那么对外访问 harbor.your-domain.com/library/nginx:latest 就会被 Harbor 去上游拉取 registry.cn-hangzhou.aliyuncs.com/library/nginx:latest 并缓存。- 将集群中的镜像地址改为 Harbor 的域名,比如 harbor.your-domain.com/library/nginx:latest。

极客栈 426

椋鸟C++笔记#4:类的初始化与封装

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 890

【第2章>第10节】深度学习DenseNet模型分析与matlab仿真

本文介绍了基于DenseNet深度学习模型的图像分类MATLAB实现方法。主要内容包括:1. DenseNet模型原理,详细解析了初始卷积层、密集块、过渡层等核心结构;2. 在MATLAB环境下完成DenseNet工具箱安装和测试样本准备;3. 实现图像预处理(统一缩放为224×224)、模型训练和测试的完整流程;4. 提供了混淆矩阵和准确率等测试结果分析。文章附带完整的MATLAB程序代码和操作视频教程,适合深度学习初学者快速掌握DenseNet模型的图像分类应用。通过实际测试,验证了该方案的有效性。

FPGA/MATLAB学习教程/源码/项目合作开发 330

椋鸟C++笔记#6:模板的基础用法

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 1075

D457深度相机调试记录(max96712+max9295A)

使用max96712解串器解4路D457相机。采集深度相机Depth和RGB流。

navy2015_163的博客 1268

北斗导航 | 接收机自主完好性监测与神经网络结合的技术路径、应用场景及研究进展

2] 许承东, 黄国限, 钟贤发, 等. 一种用于神经网络RAIM的故障卫星识别方法[P]. 中国专利: CN117192573A, 2023-12-08.随着6G通信与低轨卫星星座的融合发展,神经网络RAIM有望在2025-2030年实现从辅助技术向主导技术的跨越,成为智能导航系统的核心支撑技术。该架构采用改进型BP神经网络实现非线性映射,在GPS/北斗双模系统中实现98.7%的故障识别率[2]。注:测试条件为GPS/北斗双模系统,12颗可见卫星,含3颗故障卫星的复合场景。

尘世冰封的专栏 482

椋鸟C++笔记#5:C++内存管理

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 483

Quuppa 如何引领蓝牙 ® 定位服务的全新发展方向

我们最优质的客户,是那些对其他实时定位系统(RTLS)的承诺感到失望、转而寻求 “能覆盖全业务场景的更广泛赋能技术” 的客户 —— 前文提到的消防车制造商就是典型案例:他们尝试并通过 Quuppa 的蓝牙方案取得了成功。我们希望业务合作伙伴(如系统集成商、解决方案提供商)能充分利用 Quuppa 智能定位系统的功能,为客户提供专属解决方案:无论是追踪微型标签、实现未来移动设备的 AoD 功能,还是基于 RSSI 的简单信标(信标作为物联网网关),这些功能都能让同一套 Quuppa 系统支持广泛的用例。

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 833

椋鸟C++笔记#7:标准模板库STL初识

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 605

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

椋鸟C语言笔记#16:函数栈帧的创建与销毁

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 260

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

椋鸟C语言笔记#18:函数传值调用与传址调用、assert断言、二级指针

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 189

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

椋鸟C语言笔记#9:辗转相除法与更相减损法求公约数

萌新的学习笔记,写错了恳请斧正。

椋鸟的博客小窝 196

深挖C++:类和对象(中)

(先进后出)

xys_678的博客 251

ThunderKittens 深度解析:一个不基于 MLIR 的 C++ 模板 GPU DSL

Triton 是"让更多人能写 GPU kernel",ThunderKittens 是"让会写的人写得更爽"。前者用 MLIR 换易用性,后者用 C++ 模板换可控性。它们不是替代关系,而是覆盖了 GPU 编程光谱的两端。ThunderKittens 的出现,本质上是对"GPU DSL 必须基于 MLIR"这一默认假设的挑战。编译期类型系统本身就可以是一个强大的 DSL 基础设施,不需要中间 IR,不需要黑盒 pass,也能实现 tile 级抽象和零运行时开销。

Maxwell_Newton的博客 349

C++移动构造函数详解

移动构造函数是C++11引入的一种特殊构造函数,用于从临时对象(右值)"窃取"资源来初始化新对象,而不是进行昂贵的深拷贝。char* data;public:// 移动构造函数:参数是右值引用 &&// 关键:把源对象的资源置空。

拒绝AI文,努力在AI时代写出感动人心的好文章 394

跟我学c++中级篇—类型定义的方式

本文系统阐述了编程中类型定义的重要性及c++中的实现方式。重点对比了宏定义、typedef与modern using三种机制,指出using在可读性、灵活性和模板支持上的优势,推荐优先使用。同时提醒开发者注意别名滥用、类型歧义、错误信息冗长等问题,强调统一规范与合理设计。最终结论:在现代c++开发中,应以using为主,兼顾简洁性与可维护性,避免陷入命名混乱与编译困境。

fpcc的专栏 88

【C++——类和对象(上)】筑基篇:以封装奠定面向对象之基

本文系统梳理C++类与对象的核心知识点,涵盖类的定义、访问限定符(public/private)、类作用域、对象实例化及内存布局。重点解析封装思想、this指针机制、空类大小为1字节的原因,以及类与对象在内存中的存储差异。通过对比C语言结构体与C++类,揭示面向对象设计的优势:数据保护、接口统一、命名隔离与代码可维护性提升。内容由浅入深,适合作为学习笔记与复习参考。

2602_95684935的博客 412

C++ 函数模版

模板参数:● template ,T 就是类型占位符,名字可以自定义,习惯用T● 占位符可以在函数返回值、形参、函数内部当作类型使用使用要点模板代码一般写在头文件:编译器需要看到完整模板源码,才能在每个编译单元完成实例化全局函数、类内成员函数都可以写成模板实例化发生在编译阶段,不是运行时隐式实例化:调用时编译器自动推导实参类型,生成对应版本函数。

2501_94041724的博客 307

C++ 运行时多态与虚函数机制全面剖析【面试重点】

构成C++ 运行时多态必须是基类的指针或者引用调用虚函数。被调用的函数必须是虚函数,并且派生类必须对该虚函数完成重写/覆盖(Override)。只有基类的指针或引用才能既指向基类对象,又指向派生类对象(切片机制);而只有完成了虚函数重写,派生类与基类才具备不同的函数行为,达到“指向谁就调用谁”的效果。在类成员函数的前面加上virtual关键字修饰,该成员函数就成为了虚函数。public:// 成员函数前加 virtual 修饰,声明为虚函数。

Fuyo_0209的博客 448

#SQLite 数据库笔记一:(概念 / 终端指令 / SQL 语句 / C 编程 / 回调函数)

数据库 = 数据的仓库,假设数据是海量。能力说明存储数据持久化保存,程序退出数据还在统计数据、分组、排序数据管理增、删、改、查(CRUD)嵌入式设备上也会用数据库(单片机网关、路由器、工业 HMI 都常见),因为 sqlite 体积小、无需服务进程、单文件即可。数据库 = 库 > 表 > 记录(行) > 字段(列)sqlite3 = 开源、C 写的、单文件、免安装、最多 2T 的嵌入式关系型数据库。

lwq20051110的博客 250

1-10 PostgreSQL中多表连接全解:inner/left/right/full/cross join

本文基于 PostgreSQL 15+ 完整拆解 5 种主流连接:内连接、左连接、右连接、全连接、笛卡尔连接。结合可落地的测试数据,演示每种连接的执行逻辑、返回结果,重点剖析 NULL 值对 JOIN 匹配的核心影响,彻底解决多表连接数据丢失、匹配异常、结果错乱的问题。

zhangjw34的专栏 94
上一篇: 椋鸟C++笔记#2:从结构体到类
下一篇: 椋鸟Linux笔记#0:学习Linux前
椋鸟Starling
博客等级 码龄3年 447粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椋鸟Starling

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值