C++基础总结(上)


变量和基本类型


一、变量

  • 无符号和有符号加减会把有符号转化为无符号
  • 对于有无符号只是解释方式不同,运算法则还是补码
    区别在于把最高位是否视为符号位(示例):
    • 有符号
      ( 10001 ) 2 = − 15 (10001)_2=-15 (10001)2=15
    • 无符号
      ( 10001 ) 2 = 17 (10001)_2=17 (10001)2=17

二、字面值常量

1.进制

  • 0开头八进制,所以类似于08这种会报错
  • 0x | 0X开头16进制

2.字面值类型

  • 算术类型,指针和引用——是
  • 自定义类型,IO库,string——否

三、声明和定义

1.声明

extern int i;

2.定义

int i;

3.具体用法

两个类相互调用


四、引用和指针

1.引用

引用并非对象,定义引用就无法绑定到另外的对象,使用就是访问最初绑定的对象

2.指针

指针可以改变


五、const

1.const引用类型的转化

初始化常量引用允许任意表达式作为初始值,只要该表达式结果能转换成引用类型即可

//原理
double val = 3.14;
const int& ri = val;
		|
		V
const int temp = 3.14;
const int& ri = temp;

2.const指针

  • const int *p——指向常量的指针,底层——作用指向的对象
  • int* const p——常量指针,顶层——作用对象本身

六、处理类型

typedef,using


七、decltype

1.注意点

int i=42,&r=i,*p=&i;
decltype(r)=int&
decltype(r+0)=int
decltype(*p)=int&----解引用指针可以得到指针所指的对象的引用
decltype((i))=int&

当我们使用decltype作用于某个函数时,它返回函数类型而非指针类型,因此我们需要显示的加上*已表明我们需要返回指针

2.与auto的区别,以及和typeid的区别

如果使用引用类型,auto会识别为其所指对象的类型,decltype则会识别为引用的类型。

//有兴趣可以用编译器看一下类型
int x=10;
int &r=x;
auto b=r;
decltype(r) c=x;

typeid主要用于运行时类型识别,auto和decltype则是在编译时识别的,作用时期不同。


八、预处理器

在这里插入图片描述


字符串,向量和数组


一、string,vector,迭代器

1.string

  • getline会忽略回车符
  • size_type和有符号数混用会造成歧义,因为size_type是无符号的
  • +两侧运算对象至少一个为string

2.vector

  • ()构造,{}初始化列表
  • 使用花括号但是提供的值不能用来列表初始化,就要考虑用这样的值构造vector对象

3.迭代器

迭代器不允许相加


二、数组与指针

原则:从内到外,从右到左

int *a[10];  //a数组含有10个指针整型
int &a[10];  //错误,不存在引用的数组
int (*a)[10]; //a是一个指针,指向一个含有10个整数的数组
int (&a)[10]; //a是一个引用,引用一个含有10个整数的数组
int *(&a)[10]; // a是数组的引用,数组含有十个指针
  1. 内置下标运算符所用的索引值是有符号类型
  2. 要使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型

表达式和语句


一、表达式

  1. 左右值,左值表达的是对象本身,右值则是对象的值,举个最简单的例子,int x=5;x就是左值,而常量5就是右值
  2. 逗号运算符——返回的结果是右侧表达式的值
int y = 3;
int x = (y + 4, 5 + 6, 4 + 5);
cout << x << endl;//x=9

二、语句

  1. 范围for循环,因为预存了end()的值,所以不能通过范围for增加容器对象的元素,顺便复习一下,使用范围for循环,除了最内层,其余的范围for循环都需要使用引用类型
//范围for循环
vector<int> vec{1, 2, 3, 4, 5};
for (int it:vec)
   	cout << it << endl;
  1. 循环中需要注意的细节
    do while,do中定义的变量,while不可以使用,因为作用域的限制,do和while是不同的作用域;while和for定义的变量可以被块使用

函数


一、函数基础

1.局部静态变量

程序终止才被销毁,在此期间即使对象所在函数结束执行也不会对他有影响

#include <iostream>	

using namespace std;

void fun()
{
    static int x = 0;
    x++;
    cout << x << endl;
}

int main()
{
    fun();//1
    fun();//2
    fun();//3
}

2.参数和返回类型

形参处理不同数量的实参

  1. initializer_list,也是一个容器,在vector中讲过{ }初始化,实现原理就是内置的initializer_list,基本用法如下
void fun(initializer_list<int> vec)
{
    for (int it:vec)
        cout << it << endl;
}

int main()
{
    fun({1, 2, 3, 4, 5});
}
  1. 省略符形参…
    作用:可以暂停编译器对类型的检查
    缺点:不太安全

代码来源

int sum(int count, ...)
{  //格式:count代表参数个数, ...代表n个参数

    va_list ap;  //声明一个va_list变量
    va_start(ap, count);  //第二个参数表示形参的个数

    int sum = 0;
    for (int i = 0; i < count; i++)
    {
        sum += va_arg(ap, int);   //第二个参数表示形参类型
    }

    va_end(ap);  //用于清理

    return sum;
}

3.constexpr函数

  • 定义:函数的返回类型及所有形参类型都是字面值类型(算术类型,引用,指针),而且函数体中必须有且仅有一条return语句
  • 作用:constexpr的声明就等于告诉编译器这是在编译期就可以确定的值,你可以大胆的去优化(constexpr水挺深的,建议有兴趣可以研究,这里不过多赘述)
constexpr int fun(string &str)
{
    str = "";//正确
    str == "";//错误
    return 0;
}

原因就在于str的值是在运行期间确定的,而==需要知道str的值,但=不需要,可以在编译期完成,所以=是正确的,==是错误的

  • 与const的区别:const并不能代表“常量”,它仅仅是对变量的一个修饰,告诉编译器这个变量只能被初始化,且不能被直接修改,同时const不一定在编译期确定,也可以在运行期确定;constexpr则是相当于告诉编译器自己可以在编译期得出常量值(但有时会被欺骗,详见来源
//const运行时确定值
int main()
{
    int x;
    cin >> x;
    const int y = x;
}

二、函数重载

1.定义

  • 函数名相同,形参数量或形参类型不同(两个函数,它们的形参列表一样但是返回类型不同,则第二个函数声明错误)
int fun(int x)
{ 
    return 1; 
}

bool fun(int x)//错误
{
    return false;
}
  • 不同作用域中无法重载函数名,class A和class B中的同名fun函数不算重载

2.const形参

  • 拥有顶层const形参无法和另一个没有顶层const的形参区分开来,所以两个都出现算是重复声明,底层const不算(原理:对于重载,编译器需要根据传入的参数判断调用哪一个重载,所以需要针对实参有所区别,而非形参)
int fun(int *const x)
{
    return 1;
}

int fun(int *x)//顶层const无法通过实参匹配
{
    return 2;
}

3.匹配

  • 精准匹配
  • const类型转换:判别方式是判断实参是否是常量来决定选择哪个函数。指针类型的形参判别也是类似的(底层const)
  • 类型提升:不是完全相同的类型,都会提升类型,char会提升为int
  • 算术类型转换:该转换的级别都相同,即3.14是double,转换成long或者float都是同样等级,会产生二义性(算术类型转换的级别都一样,但好像char到int的级别比char到short的级别高,是我理解有问题还是C++ Primer讲错了???希望大佬帮忙解答一下)
int fun(long x)
{
    return 1;
}

int fun(float x)
{
    return 2;
}

int main()
{
    double x = 3.14;
    cout << fun(x) << endl;//二义性
}
  • 类类型转换,涉及多态


一、认识类

1.细节

  • 定义在类内部的函数是隐式的inline函数
  • 参数列表后的const:修改隐式this指针的类型,将其定义为const T* const this。常量对象,以及常量对象的引用或指针都只能调用常量成员函数

2.静态成员

  • 静态成员属于类,而非对象
  • 不与任何对象绑定,不包含this指针,所以不能在static函数体内使用this指针
  • 静态成员函数不能声明成const,因为const是针对对象的,但静态成员属于类

3.抑制构造函数定义的隐式转化

explicit——构造函数需要显示声明

  • explicit,初始化时该关键词无影响
  • explicit只对一个实参的构造函数有效,需要多个实参的构造函数不能用于执行隐式转化
class A
{
public:
    A(int x, int y = 0)//把=0去掉,就会报错,因为多个实参的构造函数不能用于执行隐式转化
    {

    }
};

int main()
{
    A tmp = 1;
}
  • explicit只允许出现在类内的构造函数声明或类型转换函数处
  • 用于显示声明构造函数,同时也抑制拷贝构造函数(因为拷贝构造函数在返回时会构造中间对象,这里会用到隐式的构造函数)

二、类的特性

1.可变数据成员

  • mutable类型的数据可在const函数内部修改它的值
class A
{
private:
    mutable int x, y;
public:
    void fun() const
    {
        x = 3;//正确
        y = 4;//正确
    }
};

2.类定义处理

  • 首先编译成员的声明,直到类全部可见是再编译函数体(所以函数定义可以写在外面)

3.类封装的优点

  • 封装实现了类的接口和实现的分离,隐藏了类的实现细节,用户只能接触到类的接口(后续还会设计模式的总结,面向对象真的很强大!!!)

三、友元

1.特点

  1. 只能出现在类定义内部,但类内出现的具体位置不限
  2. 友元不是类的成员,不受区域访问控制级别的约束
  3. 在友元之外再专门对函数进行一次声明

总结

以上便是C++基础总结的上篇,只是看完《C++ Primer》以后的一点小总结,内容很简陋,主要是我自己在看书过程中遇到有疑问的一些点,然后经过查阅资料以后有所收获的,在这里和大家分享一下。如果这些知识点有任何问题,欢迎大家在评论区指出,我会虚心接受…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值