C++基础总结1

本文介绍了C++编程中的一些高级技巧,包括模板传递函数、typeid关键字、可变参数函数模板、auto与decltype关键字、nullptr与NULL的区别、C++风格别名及constexpr关键字的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自己在学习C++过程中一些需要注意的地方。

1.模板传递函数
2.typeid关键字
3.可变参数函数模板
4.auto、decltype关键字
5.C++中的nullptr与NULL
6.C++风格别名
7.constexpr关键字

1.模板传递函数

例程:

#include <iostream>

using namespace std;

template<typename T>//摸板函数
void show(T t)
{
    cout << t << endl;
}

template <typename T, typename F>
void run(T t, F f)//模板函数,第二个参数传递函数
{
    f(t);
}

int main()
{
    run(10, show<int>);
    run("abc", show<const char*>);
    return 0;
}

输出结果为:

10
abc

2.typeid关键字

typeid关键字返回变量的类型名:例如
#include <iostream>

//typeid是关键字,不是函数
int main()
{
    int a;
    std::cout << typeid(a).name() << std::endl;
    std::cout << typeid(10).name() << std::endl;
    std::cout << typeid(10.0).name() << std::endl;
    std::cout << typeid("wangpeng").name() << std::endl;
    return 0;
}

运行结果:

typeid关键字

3.可变参数函数模板

第一种情况:参数类型一致

#include <iostream>
#include <cstdarg>
using namespace std;

template <typename T>
T add(int n, T t...)//求n个数的和
{
    cout << typeid(T).name() << endl;

    va_list arg_ptr;//开头指针
    va_start(arg_ptr, n);//开始从头读取n个
    T res(0);//定义变量res初始化为0
    for (int i = 0; i < n; i++)
    {
        res += va_arg(arg_ptr, T);//根据数据类型取出数据
    }
    va_end(arg_ptr);

    cout << res << endl;
    return res;
}

int main()
{
    add(4, 1, 2, 3, 4);
    add(3, 10.2, 3.4, 5.0);

    return 0;
}

运行结果:
这里写图片描述

第二种情况:参数类型不一致

#include <iostream>
#include <cstdarg>
using namespace std;

void show()//结束递归
{

}
//参数类型不一致
template<typename T, typename...Args>//typename...Args可变参数
void show(T t, Args... args)
{
    cout << t << " ";
    show(args...);
}

int main()
{
    show(2, 3.5, "ABS", 'a');

    return 0;
}

运行结果:
这里写图片描述

应用实例:用C++可变参数模板实现C语言printf功能

#include <iostream>
#include <cstdarg>
using namespace std;

void print(const char *str)
{
    cout << str;
}

template<typename T, typename...Args>//typename...Args可变参数
void print(const char *str, T t, Args... args)
{
    while (str && *str)//字符串不为空且没到末尾
    {
        if (*str == '%'  && *(str + 1) != '%')
        {
            ++str;
            cout << t;
            print(++str, args...);//继续调用
            return;
        }
        else if (*str == '%'  && *(str + 1) == '%')
        {
            str++;
            cout << *str++;
        }
        else
        {
            cout << *str++;
        }
    }
}

int main()
{
    printf("%dABCD%s%c%%%fXXX\n", 10, "1234", '0', 123.5);
    print("%dABCD%s%c%%%fXXX\n", 10, "1234", '0', 123.5);
    return 0;
}

输出结果
这里写图片描述

4.auto、decltype关键字

1、auto是修饰未知变量的类型,编译器会通过此变量的初始化自动推导变量的类型,示例1

#include <iostream>

using namespace std;

int main()
{
    auto num = 10;//auto自适应数据类型
    cout << typeid(num).name() << endl;

    int a[5]{1, 2, 3, 4, 5};
    for (auto i : a)//auto i为副本,不会改变数组内容
    {
        cout << i << " ";
    }
    cout << endl;
    for (auto &i : a)//auto &i为原本,会改变数组内容
    {
        i++;
        cout << i << " ";
    }
    cout << endl;
    return 0;
}

运行结果:
这里写图片描述

示例2:自动推断函数模板返回类型

#include <iostream>

using namespace std;

//C++14自动推理返回值,不需要->decltype(t1+t2)
//C++11需要->指定类型
template <class T1, class T2>
auto add(T1 t1, T2 t2)->decltype(t1+t2)
{
    return t1 + t2;
}

int main()
{
    cout << add(10, 2.5) << endl;
    return 0;
}

程序运行结果:
这里写图片描述

2、关键字decltype能够推导表达式或者函数的返回类型,但不对表达式求值。示例:

#include <iostream>

using namespace std;

int main()
{
    auto boer("hello world");
    auto afu(1 && 0 && 3 || -1);//afu为bool类型
    cout << typeid(afu).name() << "   " << afu << endl;
    decltype(afu) afu2[5]{0,1,2,3,4};//decltype(afu)根据afu的类型自动获取类型
    for (auto i : afu2)
    {
        cout << i << " ";
    }

    return 0;
}

程序运行结果:
这里写图片描述

decltype的推导结果还与给定的表达式的形式有关。如果对变量求类型,那么decltype直接返回变量的类型;如果变量加括号后,那么decltype返回的类型是引用,引用的类型就是该变量的类型。

decltype(i) e;     // e is an int variable uninitialized  
decltype((i)) d;  // error: d is int & and must be initialized

5.C++中的nullptr与NULL

引入nullptr的原因,这个要从NULL说起。对于C和C++程序员来说,一定不会对NULL感到陌生。但是C和C++中的NULL却不等价。NULL表示指针不指向任何对象,但是问题在于,NULL不是关键字,而只是一个宏定义(macro)。在C中,习惯将NULL定义为void*指针值0,但同时,也允许将NULL定义为整常数0。

在C++中,NULL却被明确定义为整常数0,这样做的根本原因和C++的重载函数有关。C++通过搜索匹配参数的机制,试图找到最佳匹配(best-match)的函数,而如果继续支持void*的隐式类型转换,则会带来语义二义性(syntax ambiguous)的问题。例如:

#include <iostream>

using namespace std;

void show(int a)
{
    cout << "int" << endl;
}

void show(int* a)
{
    cout << "int *" << endl;
}

int main()
{
    show(NULL);//C风格空指针,打印int
    show(nullptr);//C++风格指针,打印int *

    cout << typeid(NULL).name() << endl;
    cout << typeid(nullptr).name() << endl;
    return 0;
}

这里写图片描述

6.C++风格别名

在C语言中定义别名用typedef,在C++一般用using。

1.简单类型别名:

typedef int a[5]; //C风格别名
using arr = int[5];//C++

typedef double db;//C 风格别名
using DBCPP = double;//using C++风格别名

typedef int(*p)(int a, int b);//函数指针类型,p
using pfun = int(*)(int a, int b);

2.array数组模板类型别名(类型明确的情况):

#include <iostream>
#include <array>

using namespace std;

int main()
{
    using intarray = array<int, 10>;//模板别名,明确类型
    intarray my{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    for (auto i : my)
    {
        cout << i << "  ";
    }
    cout << endl;
    return 0;
}

3.array数组模板类型别名(类型不明确的情况):

#include <iostream>
#include <array>

using namespace std;

//模板的别名:用模板名优化模板的名称,类,命名空间只能放在全局,不能放在函数内部
template<typename T> 
using myarray = array<T, 5>;
int main()
{
    myarray<int> arr{ 1, 2, 3, 4, 5 };//模板别名,不明确类型
    myarray<char> charr{ 'a', 's', 'd', 'f', 'e' };//模板别名,不明确类型
    for (auto i : arr)
    {
        cout << i << "  ";
    }
    cout << endl;
    for (auto i : charr)
    {
        cout << i << "  ";
    }
    cout << endl;

    return 0;
}

4.在命名空间中定义的函数模板中使用别名

#include <iostream>
#include <array>

using namespace std;

namespace china
{
    template<typename T> using t = T;
    template<typename T> using tp = T*;

    template<typename T>
    T add(T a)
    {
        t<decltype(a)> c(a);//使用模板别名时必须用<decltype(a)>明确指明类型
        tp<decltype(a)> cp(&a);
        cout << "模板别名:" << c << "  " << cp << endl;
        return c;
    }
}

int main()
{
    cout << china::add(1) << endl;
    return 0;
}

7.constexpr关键字

constexpr是C++11中新增的关键字,其语义是“常量表达式”,也就是在编译期可求值的表达式。最基础的常量表达式就是字面值或全局变量/函数的地址或sizeof等关键字返回的结果,而其它常量表达式都是由基础表达式通过各种确定的运算得到的。constexpr值可用于enum、switch、数组长度等场合。
constexpr所修饰的变量一定是编译期可求值的,所修饰的函数在其所有参数都是constexpr时,一定会返回constexpr。

constexpr int Inc(int i) {
    return i + 1;
}

constexpr int a = Inc(1); // ok
constexpr int b = Inc(cin.get()); // !error
constexpr int c = a * 2 + 1; // ok

constexpr还能用于修饰类的构造函数,即保证如果提供给该构造函数的参数都是constexpr,那么产生的对象中的所有成员都会是constexpr,该对象也就是constexpr对象了,可用于各种只能使用constexpr的场合。注意,constexpr构造函数必须有一个空的函数体,即所有成员变量的初始化都放到初始化列表中

struct A {
    constexpr A(int xx, int yy): x(xx), y(yy) {}
    int x, y;
};

constexpr A a(1, 2);
enum {SIZE_X = a.x, SIZE_Y = a.y};

constexpr的好处:

  1. 是一种很强的约束,更好地保证程序的正确语义不被破坏。
  2. 编译器可以在编译期对constexpr的代码进行非常大的优化,比如将用到的constexpr表达式都直接替换成最终结果等。
  3. 相比宏来说,没有额外的开销,但更安全可靠。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值