c++随笔-7

news/2025/1/15 19:51:45/

函数模板

// 函数模板示例
template <typename T1, typename T2>
void Swap(T1 &a, T2 &b) {  // typename 也可使用 class 替换,推荐使用 typenamea = a + b;b = a - b;a = a - b;std::cout << "template Swap a: " << a << "\tc: " << b << std::endl;
}void Swap(int &a, int &b) {   // 重载函数模板 Swap 函数a = a + b;b = a - b;a = a - b;std::cout << "Swap a: " << a << "\tb: " << b << std::endl;
}int main(int argc, char *argv[])
{int a = 10;int b = 20;short c = 'C';// 调用规则// 1. 编译器优先调用普通匹配函数// 2. 如果函数模板严格匹配,则调用函数模板// 3. 通过空模板实参限定编译器只通过模板匹配Swap(a, b);   // 有匹配的普通函数,调用普通函数(包括隐式转换)Swap(a, c);   // 函数模板进行函数调用时进行严格的类型匹配Swap<>(a, b); // 限定使用函数模板调用// Swap<short, short>(a, b);  // 参数类型和实际变量类型不匹配return 0;
}

输出结果:
Swap a: 20 b: 10
template Swap a: 67 c: 20
template Swap a: 10 c: 67

  • 模板包含函数模板和类模板,本质是类型参数化
  • 模板提供了一种将类型参数化的机制,减少代码的冗余
// 对应上边代码的汇编片段23 [1]	{
0x401604                  55                    push   %rbp
0x401605  <+    1>        48 89 e5              mov    %rsp,%rbp
0x401608  <+    4>        48 83 ec 30           sub    $0x30,%rsp
0x40160c  <+    8>        89 4d 10              mov    %ecx,0x10(%rbp)
0x40160f  <+   11>        48 89 55 18           mov    %rdx,0x18(%rbp)
0x401613  <+   15>        e8 b8 01 00 00        callq  0x4017d0 <__main>24 [1]	    int a = 10;
0x401618  <+   20>        c7 45 fc 0a 00 00 00  movl   $0xa,-0x4(%rbp)25 [1]	    int b = 20;
0x40161f  <+   27>        c7 45 f8 14 00 00 00  movl   $0x14,-0x8(%rbp)26 [1]	    short c = 'C';
0x401626  <+   34>        66 c7 45 f6 43 00     movw   $0x43,-0xa(%rbp)32 [1]	    Swap(a, b);   
0x40162c  <+   40>        48 8d 55 f8           lea    -0x8(%rbp),%rdx
0x401630  <+   44>        48 8d 45 fc           lea    -0x4(%rbp),%rax
0x401634  <+   48>        48 89 c1              mov    %rax,%rcx
0x401637  <+   51>        e8 24 ff ff ff        callq  0x401560 <Swap(int&, int&)>33 [1]	    Swap(a, c);  
0x40163c  <+   56>        48 8d 55 f6           lea    -0xa(%rbp),%rdx
0x401640  <+   60>        48 8d 45 fc           lea    -0x4(%rbp),%rax
0x401644  <+   64>        48 89 c1              mov    %rax,%rcx
0x401647  <+   67>        e8 24 18 00 00        callq  0x402e70 <Swap<int, short>(int&, short&)>34 [1]	    Swap<>(a, b); 
0x40164c  <+   72>        48 8d 55 f8           lea    -0x8(%rbp),%rdx
0x401650  <+   76>        48 8d 45 fc           lea    -0x4(%rbp),%rax
0x401654  <+   80>        48 89 c1              mov    %rax,%rcx
0x401657  <+   83>        e8 64 17 00 00        callq  0x402dc0 <Swap<int, int>(int&, int&)>36 [1]	    return 0;
0x40165c  <+   88>        b8 00 00 00 00        mov    $0x0,%eax37 [1]	}
0x401661  <+   93>        48 83 c4 30           add    $0x30,%rsp
0x401665  <+   97>        5d                    pop    %rbp
0x401666  <+   98>        c3                    retq
  • 重点看 callq,也就是函数调用,将参数类型泛化可知,泛化类型至少包含好几种参数类型,猪观理解是c++在编译阶段会创建和所包含类型相关的所有重载函数,而实际上c++对模板的处理采取二次编译,c++在初次编译阶段并未创建模板包含类型的重载函数,而仅仅只是对模板编写语法等分析,确保编译阶段通过,调用时c++对模板调用进行二次编译,且仅仅只创建生成具体的模板,不会创建其它类型

类模板

// 类模板示例
template <typename T>
class CBase {
public:void SetId(T id) {  // 类中实现模板类成员函数m_id = id;}void Print();
private:T m_id;
};template <typename T>
void CBase<T>::Print() {  // 类外实现模板类成员函数,在同一个文件中的写法std::cout << "id : " << m_id << std::endl;
}int main(int argc, char *argv[])
{CBase<int> base;  // 创建对象时必须指定类型base.SetId(10);base.Print();return 0;
}

输出结果:
id : 10

// 从模板类派生普通类
template <typename T>
class CBase {
public:void SetId(T id) {  // 类中实现模板类成员函数m_id = id;}void Print();
private:T m_id;
};class CDerive : public CBase<int> {  // 类模板 继承
public:};template <typename T>
void CBase<T>::Print() {  // 类外实现模板类成员函数,在同一个文件中的写法std::cout << "id : " << m_id << std::endl;
}int main(int argc, char *argv[])
{CDerive d;  // 创建对象时必须指定类型d.SetId(10);d.Print();return 0;
}

输出结果:
id : 10

// 从模板类派生模板类
template <typename T>
class CBase {
public:void SetId(T id) {  // 类中实现模板类成员函数m_id = id;}void Print();
private:T m_id;
};template <typename T>
class CDerive : public CBase<T> {  // 类模板 继承
public:};template <typename T>
void CBase<T>::Print() {  // 类外实现模板类成员函数,在同一个文件中的写法std::cout << "id : " << m_id << std::endl;
}int main(int argc, char *argv[])
{CDerive<int> d;  // 创建对象时必须指定类型d.SetId(10);d.Print();return 0;
}

输出结果:
id : 10

  • 模板类和函数模板一样,都是类型参数化,只不过类模板语法规则复杂
  • 模板类也可以继承,必须参数具体化,如果需要显式初始化,派生类需要调用基类构造函数,默认调用无参构造
  • 模板类可派生普通类,也可派生模板类
template <typename T>
class CBase {
public:void SetId(T id) {  // 类中实现模板类成员函数m_id = id;}void Print();template <typename>friend std::ostream &operator<< (std::ostream &os, CBase<T>& obj);T m_id;template <typename>friend void Report(CBase<int>& obj);
};template <typename T>
void CBase<T>::Print() {  // 类外实现模板类成员函数,在同一个文件中的写法std::cout << "id : " << m_id << std::endl;
}template <typename T>
std::ostream & operator<<(std::ostream &os, CBase<T>& obj) {os << "id : " << obj.m_id << std::endl;return os;
}void Report(CBase<int>& obj) {std::cout << "id : " << obj.m_id << std::endl;
}int main(int argc, char *argv[])
{CBase<int> base;  // 创建对象时必须指定类型base.SetId(10);base.Print();std::cout << base;Report(base);return 0;
}

输出结果:
id : 10
id : 10
id : 10

  • 友元函数使用在模板类中比较复杂,防止滥用友元
template <typename T>
class CBase {
public:static int m_id;
};template <typename T>
int CBase<T>::m_id = 10;int main(int argc, char *argv[])
{printf("int %#p\n", &CBase<int>::m_id);printf("double %#p\n", &CBase<double>::m_id);return 0;
}

输出结果:
int 0x408120
double 0x408110

  • 模板类使用 static 关键字时,不同的具体类有各自的 static 变量

异常

// 模拟抛出异常
void Exception(int i) {if (i == 10)throw "i == 10";
}int main(int argc, char *argv[])
{try {Exception(10);} catch (const char* e) {std::cout << e << std::endl;}return 0;
}

输出结果:
i == 10

  • 异常与函数独立互补,函数出错可抛出异常,异常捕获方式是严格基于类型匹配的
void Exception(int i) {if (i == 10)throw "i == 10";
}void Throw() {try {Exception(10);} catch (const char* e) {std::cout << "throw e : " << e << std::endl;throw e;  // 继续抛出异常,不处理}
}int main(int argc, char *argv[])
{try {Throw();} catch (const char* e) {std::cout << e << std::endl;}return 0;
}

输结果:
throw e : i == 10
i == 10

  • 异常抛出如果不处理程序中断,不处理异常可以继续向上抛出异常
// 栈解旋
class CBase {
public:CBase() {std::cout << "constructor" << std::endl;}~CBase() {std::cout << "destructor" << std::endl;}
};
void Exception(int i) {CBase t;if (i == 10)throw "i == 10";
}int main(int argc, char *argv[])
{try {Exception(10);} catch (const char* e) {std::cout << e << std::endl;}return 0;
}

输出结果:
constructor
destructor
i == 10

  • 异常被抛出后,从进入 try 语句块起,到异常被抛出前,在这期间在栈上创建的所有对象都会被自动析构,析构顺序与构造顺序相反,这一过程称为栈解旋
// 异常接口
void Exception1(int i) {  // 默认可抛出任何异常if (i == 10)throw "i == 10";
}void Exception2(int i) throw (int){  // 默认可抛出 int 异常if (i == 10)throw "i == 10";
}void Exception3(int i) throw (){  // 默认不抛出任何异常if (i == 10);
}int main(int argc, char *argv[])
{try {
//        Exception1(10);
//        Exception2(10);Exception3(10);} catch (...) {;}return 0;
}
// 抛出异常类
class CException {};void Exception(int i) {  if (i == 10)throw CException();  // 抛出异常类
}int main(int argc, char *argv[])
{try {Exception(10);} catch (CException e) {std::cout << "recv exception" << std::endl;}return 0;
}
// catch 接收异常类,普通变量方式
class CException {
public:CException() {std::cout << "CException()" << std::endl;}CException(const CException&) {std::cout << "CException(const CException&)" << std::endl;}~CException() {std::cout << "~CException" << std::endl;}
};void Exception(int i) {  // 默认可抛出任何异常if (i == 10)throw CException();    // 匿名对象
}int main(int argc, char *argv[])
{try {Exception(10);} catch (CException e) {   // 调用拷贝构造函数捕获抛出的异常类对象std::cout << "recv exception" << std::endl;}return 0;
}

输出结果:
CException()
CException(const CException&)
recv exception
~CException
~CException

// catch 接收异常类,引用方式
class CException {
public:CException() {std::cout << "CException()" << std::endl;}CException(const CException&) {std::cout << "CException(const CException&)" << std::endl;}~CException() {std::cout << "~CException" << std::endl;}
};void Exception(int i) {if (i == 10)throw CException();
}int main(int argc, char *argv[])
{try {Exception(10);} catch (const CException &e) {  // 匿名对象初始化引用,同一块空间std::cout << "recv exception" << std::endl;}return 0;
}

输出结果:
CException()
recv exception
~CException

// catch 接收异常类,指针方式
class CException {
public:CException() {std::cout << "CException()" << std::endl;}CException(const CException&) {std::cout << "CException(const CException&)" << std::endl;}~CException() {std::cout << "~CException" << std::endl;}
};void Exception(int i) {  // 默认可抛出任何异常if (i == 10)throw new CException(); // 申请堆内存
}int main(int argc, char *argv[])
{try {Exception(10);} catch (const CException *e) {std::cout << "recv exception" << std::endl;delete e;  // 释放内存}return 0;
}

输出结果:
CException()
recv exception
~CException


文件操作

文件打开模式描述
ios::in只读
ios::out只写
ios::app从末尾追加
ios::binary二进制模式
ios::nocreate打开文件时,文件不存在,不创建文件
ios::noreplace打开文件时,文件不存在,则创建该文件
ios::trunc打开文件后,清空文件内容
ios::ate打开文件后,将文件指针位置移动到文件末尾
文件指针位置描述
ios::beg文件开始
ios::end文件末尾
ios::cur当前位置
#include <vector>
#include <string>
#include <fstream>
#include <iostream>int main()
{// 读写文本文件std::ifstream in("hello.txt");std::ofstream out("out.txt", std::ios::app);if (!in.is_open()) {std::cout << "open file failed" << std::endl;return -1;}std::string temp;while(getline(in, temp)) {out << temp;out << std::endl;}in.close();out.close();return 0;
}
int main()
{// 写二进制文件std::ofstream out("out.dat", std::ios::binary);int num = 20;std::string str("Hello, World");out.write((char*)&num, sizeof(int));out.write(str.c_str(), str.size());out.close();// 读二进制文件std::ifstream in("out.dat", std::ios::binary);char buf[256] = {0};in.read((char*)&num, sizeof(int));in.read(buf, 256);std::cout << "int : " << num << std::endl;std::cout << "str : " << buf << std::endl;in.close();return 0;
}

输出结果:
int : 20
str : Hello, world

// 读取终端输入保存到文件
int main()
{std::ofstream out("out.txt", std::ios::app);std::string str;std::cin >> str;   // 会在空格处截断输入的内容out.write(str.c_str(), str.size());out.close();return 0;
}
  • 注意 std::cin 的用法,获取终端输入一行的内容可使用 getline 函数


http://www.ppmy.cn/news/924793.html

相关文章

c/c++论坛及网址

以下都是在网上查到的c/c学习的论坛和网址&#xff0c;今天记下来了。 呵呵。o(∩_∩)o... 孙鑫vc视频教程 http://www.codeguru.cn/VC%26MFC/sunxinvc C函数实例参考手册 http://www.codeguru.cn/CPP/CExample/ C/C实例参考手册 http://www.codeguru.cn/CPP/cppExample/ Win…

浅析编译与链接

生成可执行文件的四个过程 当编写和构建计算机程序时&#xff0c;预处理、编译、汇编和链接是将源代码转化为可执行程序的关键过程。以下是对每个阶段的详细解释&#xff1a; 1. 预处理&#xff08;Preprocessing&#xff09;&#xff1a;将.c/.cpp文件中的头文件展开、宏展开…

【ARM Coresight 系列文章 10 - ARM Coresight STM 介绍及使用】

文章目录 ARM System Trace MacrocellSTM FeaturesSTM 与 ETM/PTM的差异STM Master ARM System Trace Macrocell ARM 对STM 的解释是其支持高带宽的"仪器化输出"&#xff0c;仪器化输出其实也就是像 Cortex-M 系列中的 ITM 一样&#xff0c;通过将数据写入 STM 的 s…

打造高端定制商旅服务,海航好不好

面对高端定制旅游市场需求的持续膨胀&#xff0c;海航陈峰指导海航通航紧抓发展机遇&#xff0c;利用旗下世界领先的公务机资源&#xff0c;不断进行高端商旅配套服务的开发应用&#xff0c;那么海航好不好呢&#xff0c;了解了才知道。   海航通航凭借其在高端商旅业界的领…

香港服务器怎么样?到底好不好?

香港服务器怎么样&#xff1f;到底好不好&#xff1f;香港服务器和国内服务器&#xff0c;都是企业常用的平台。本质基本上一致&#xff0c;但是他们之间的区别在哪里了&#xff1f; 一、香港服务器有哪些优势? 线路方面。香港云服务器不存在国内电信和联通互联不互通的问题&…

安卓触控一体机为什么得到大家认可?远比Windows系统一体机大受欢迎

1、系统稳定性windows一体机&#xff1a;window系统主要针对桌面电脑应用开发&#xff0c;满足办公&#xff0c;娱乐需求&#xff0c;通过键盘、鼠标操作&#xff0c;在触摸方面从windows10系统表现才好一些Android一体机&#xff1a;安卓系统本身是为触摸而生&#xff0c;因此…

Oracle Exadata一体机与云计算应用(二)

3. Exadata主要技术 3.1 单元分流 在数据库云服务器中&#xff0c;存储并不只是转储存储。存储单元有足够的智能在内部处理某些负载&#xff0c;这样可以减轻数据库节点的工作。此过程被称作单元分流。 3.2 智能扫描 智能扫描是 Exadata最重要的一个功能&#xff0c;它的作用就…