C++primer知识点(一)

一:

g++c1.cpp -o c1.exe -std=c++11

c++11,加入了int a = {10},的初始化形式,如果有精度损失,那么会有警告。

二:

对:const int引用=  int //const 只是针对自己来说的

错:int 引用 = constint //不符合逻辑与语法,引用不是常量,说明可以改,但引用的却是常量,矛盾了。

三:

引用的初始化(绑定)必须是类型一致:除了

1:常量引用可以是,可转换成引用的类型的。

因为是常量引用,既然不能改,编译器的实现方式是借助一个临时常变量。

double dval = 3.14;

const int &ri = dval;

实际却是:

const int temp = dval;

const int &ri = temp;

其实是绑定到了一个临时量。既然不能改,也合理。(指针没这么做),这方面指针是错误,引用是警告。

2:继承关系 &父 = 子。

四:

int i =-1;

constint ic = 10;

constint *p1 =&i;

int*const p2 =&i;

constint *const p3 = ⁣

//注释的都是我预估错了的

i= ic;//y,普通变量可以,对于引用和指针不行

p1= p3;//y

p1 = ⁣//y

//p3 = ⁣//no p3的指向不能变了

//p2 = p1;//no

//ic = *p3;//no ic的值不能变了

五:

顶层const,对变量本身而言

底层const,与指针,引用等复合类型的基本类型部分有关。用于声明引用的const,都是底层const

别名声明: using SI = Sales_item;  相当于 typedefSales_item SI;

typedef char *pstring;

const pstring cstr = 0; 相当于 char * const cstr =0;

注意,pstring 基本数据类型是char , 而如果是 const char * cstr =0;基本数据类型就成了 const char。

六:处理类型

(1)auto

1:对于普通的类型就是值赋值

2:对于复合数据类型,因为赋值之后,还要有关系,所以需要注意

3:(书上说对顶层const忽略,底层const保留,想起来比较复杂,暂时忽略)

例如:

int i = 0;

const int ci = i,&cr = ci;

auto b=ci;//b是一个整数

auto c = cr;//c是一个整数(引用只是别名,赋完值就没什么关系了)

auto d = &i;//d是一个int 指针

auto e = &ci;//e是一个指向整数常量的指针(赋完值还有关系推断)

(2)decltype

返回实际的类型,例如 const int i = 1;decltype(i) x= 0;//x是const int

1:解引用操作返回的是引用类型。

int i = 20,*p=&i;

decltype(*p) c = &i;//c是int&类型,必须初始化

2:如果给变量(表达式)加括号,会得到引用类型

decltype((i)) d = &i;//d是int&类型,必须初始化

七:初始化

c++11 类内成员可以提供一个初始值。

1:拷贝初始化(=)只能提供一个值

2:类内初始值,只能使用 = 或者{ },不能用( ),防止用类型初始化当成函数。有歧义。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2628.html#konaissue

3:初始元素值的列表,只能用{ }花括号。(多个)如vector(string不是列表,所以可以)

八:

string str = “Hello”;

str[n] //返回str中第n个元素的引用。

拷贝初始化 =

(值/默认)直接初始化 () 或{},例:只提供数量 如 vector<int> vec1(10);//10是数量默认初始化。

列表初始化 { }

如果循环内部有增删vector的元素,那么不能使用“范围for”,因为对应的迭代器会有失效问题。

vector对象类型总是包含着元素的类型。

vector<int>::size_type //正确 (无符号类型)

vector::size_type //错误

两个迭代器的距离类型:difference_type (带符号整型)

两个指针相减的类型 ptrdiff_t (带符号类型)

下标类型size_t (无符号)但内置的下标类型可以取负数。(这个没那么严格,取得不对就是未定义的错误)

constexpr //顶层const

如:constexpr int *p = &i; 等于 int * const p = &i;

定义数组时必须指定类型,不允许使用auto根据列表判断类型。

数组名在auto推断时,只是一个普通的指针,并不是const指针

intia[] = {0,1,2};

autoia2(ia); //类似于autoia2(&ia[0]);

intit = 10;

ia2= &it;

cout<<*ia2<<endl;

decltype返回的类型最精确:

intia[] = {0,1,2};

decltype(ia)ib = {5,6,8};

for(autoc:ib)

cout<<c<<endl;

九:

无法保证c_str()返回的数组一直有效。

用数组初始化vector,只需指明要拷贝区域的首尾地址即可。

int arr[] = {0,1,2};

vector<int>ivec(begin(arr),end(arr));

十:

所有的范围for都可以改写成普通的for(i,j)。

但是引用却不一定都可以改写成指针。毕竟不是同一个东西,各有各的用途。就好象数组名在作为参数传递时会蜕变成指针,但是并不是说任何地方都可以替换成指针,比如sizeof就没法互相替换。

防止数组被自动转换成指针,可以通过引用实现。

(使用范围for处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型)

for(auto &row:ia)// 不然会自动转换,导致编译不过

for(autocol:row)

数组其实有类型,只是我们只在定义的时候会用到,其他情况(强转成数组类型不行)。

#include<iostream>

using namespace std;

int main()

{

intia[2][3] = {

{0,1,2},

{3,4,5}

};

//using int_array = int[3];//类型别名

//typedefint int_array[3];

//for(int_array*p=ia;p!=ia+2;++p)

// for(autop=begin(ia);p!=end(ia);++p)

for(autop=ia;p!=ia+2;++p)//auto p可以替换成int(*p)[3]

{

//for(autoq=begin(*p);q!=end(*p);++q)

for(autoq=*p;q!=*p+3;++q)//auto q可以替换成int *q

{

cout<<*q<<‘‘;

}

cout<<endl;

}

return0;

}

//int(*p)[3]这就是数组类型,指针是替换不了的。

//using int_array = int[3];//类型别名

十一:

并不是所有的数组都可以用指针替代

并不是所有的引用都可以用指针替代

#include<iostream>

using namespace std;

int main()

{

int ia[2][3] = {

{0,1,2},

{3,4,5}

};

//范围for处理多维,必须是引用

//auto &row:ia

for(int (&row)[3]:ia)//int (*row)[3]:ia)

{

for(int col:row)

{

cout<<col<<‘ ‘;

}

cout<<endl;

}

return 0;

}

引用的一大好处就是,避免元素的拷贝,节省空间和效率。

比如对于vector<string>vec的范围for遍历

for(auto &c:vec)//避免拷贝。这里不能改成指针,因为vector存的不是地址

十二:

除法运算,商的符号(两个运算对象符号相同,则为正,相反,则为负)

模除,符号与被除数的符号一样。因为要符合m = (m/n)*n+m%n 的原则。

类型转换的趋势:向无符号,向大空间 转化

decltype

&取地址

sizeof

typeid

数组名到指针的自动转化不会发生。

强制类型转换:

(1)static_cast

(2)const_cast只能改变运算对象的底层const

如:constchar *pc; char *p = const_cast<char*>(pc);可以通过p写了

(3)reinterpret_cast(为运算对象的位模式提供较低层次上的重新解释。

如:int*ip; char *pc=reinterpret_cast<char*>(ip);

十三:

initializer_list 与vector不一样的是前者永远是“常量值”

范围for的条件是对象要有bengin和end函数,数组和string及initializer_list都有。

varargs:

void CLogFile::vWriteLogEX (constchar *format,...)

{

char szMsg[MAX_LOG_LENGTH + 1];

memset(szMsg,0,sizeof(szMsg));

/* 内容 */

va_list argp;

va_start(argp,format);

//还是调用库函数对不定参数解析,而不是自己解析    _vsnprintf_s(&szMsg[strlen(szMsg)],MAX_LOG_LENGTH,_TRUNCATE,format, argp);

va_end(argp);

m_queueQueryMsg.push(szMsg);

}

递归函数:N的阶乘。

平时总是这么写:

int fun(int n)

{

if(n==1) return 1;

else return fun(n-1)*n;

}

先处理终结条件。没有任何对异常输入的判断(负数等)

另一种写法:

int fun(int n)

{

if(n > 1) return fun(n-1)*n;

return 1;

}

十四:

返回数组指针:

#include<iostream>

#include<string>

#include<vector>

#include<stdexcept>

usingnamespace std;

int odd[]= { 1, 3, 5 };

inteven[] = { 0, 2, 4 };

int(*fun(inti))[3]

{

returni % 2 ? &odd : &even;

}

//尾置返回类型

auto fun2(inti) ->int(*)[3]

{

returni % 2 ? &odd : &even;

}

decltype(odd) *fun3(inti)

{

returni % 2 ? &odd : &even;

}

usingint3Arr =int(*)[3];

int3Arr fun4(inti)

{

returni % 2 ? &odd : &even;

}

int main(intargc,char
**argv)

{

int (*ret)[3] = fun3(2);

for (int i = 0; i < 3;++i)

{

cout << (*ret)[i]<<‘ ‘;

}

return 0;

}

顶层const(不影响传入函数的对象)不能实现重载,底层const可以实现重载(指针const int * p ,和引用)

编译器确定重载函数的过程:

1:候选函数(可见作用域内,相同的函数名字)

2:可行函数(参数数量,和类型找最优)

内联机制用于优化“规模较小,流程直接,频繁调用”的函数。

常量表达式:由类型和初始值确定。

constexpr函数:能用于常量表达式的函数。要求:

(1)函数的返回类型及形参的类型都得是字面值类型。(算术,引用,指针)(string,io,自定义类型都不行)

(2)函数体有且只有一条return 语句。(也可以有运行时不执行任何操作的空语句,类型别名和using声明)

constexpr函数不一定返回常量表达式。constexprint fun(int i){return i*2};认为这不是常量表达式。

constexpr函数被隐式的指定为内联函数(为在编译过程中随时展开)

函数指针:

usingFun =void(long);

usingPFun =void(*)(long);

函数可以返回PFun类型,不能返回Fun类型。但是作为函数的参数,Fun会自动转成函数指针。(也就是说参数是Fun,PFun的函数是同一个)

typedef int(*Fun)(int);

auto fun(int) ->int(*)(int) //尾置返回类型。

decltype(fun)

十五:类

抽象:数据成员,成员函数

封装:访问属性(private,protect,public)

this 是常量指针,但不是指向常量的指针。根据初始化规则,我们不能把this绑定到一个常量对象上。

不能在常量对象上调用普通成员函数。(this的类型不同),常量对象(引用和指针只能访问常量成员函数)

常量成员函数的const的作用就是把this声明成指向常量的指针。所以编译器会对const成员函数做限制,对象的属性都是const的,不能访问普通成员函数。

编译器分两步处理类:1:处理所有声明2 :再处理函数体(函数体在整个类可见后才会被处理)

IO类属于不能被拷贝的类型,并且读取和写入会改变流的内容,所以

1:只能用引用传参

2:不能是const引用

对于一个普通的类必须定义自己的构造函数:

1:如果我们自己定义了构造函数,编译器就不会再定义默认构造函数。(依据:如果一个类在某种情况需要控制对象初始化,那么可能在所有情况都需要控制);

2:内置类型或复合类型(数组,指针),默认的构造函数中值是未定义的。

3:包含其他类类型的成员,而那个类没有默认构造函数。

如果我们需要默认的行为,就在参数列表后面加上 = default;这样就会有默认构造函数了。

拷贝,赋值,销毁 ;编译器会替我们合成,但是有时候合成的版本不能正在工作,特别是需要分配类对象之外的资源时(如管理动态内存的类),vector和string可以。

友元不受访问控制级别的限制。因为他不是类的成员。

友元仅仅指定了访问权限,并不是声明。所以必须在友元声明之外再单独声明。

友元没有传递性。

类和非成员函数的声明不是必须在他们的友元声明之前,编译器会隐式假定友元的声明名字在作用域内是可见的。即使在类的内部定义该函数,也必须在类外提供相应的声明,使函数可见。

友元类的“成员函数”(对象不可以)可以访问此类的非公有成员,所以,可以认为友元类的作用是把自己的所有的成员函数声明称另一个类的友元。

mutable可变数据成员,即使const成员函数也可以改变他的值。(而volatile由程序直接控制之外的过程控制,如系统时钟更新的变量,mutable是在程序控制之中的改变。)

对于常量函数的const可以重载(底层const可以重载)

类没定义,只是声明 :不完全类型。

不完全类型:(1)可以定义指针和引用。(2)可以声明(但不能定义)以他为参数或返回类型的函数。

函数的作用域运算符只限定参数列表和函数体,如果返回类型也是作用域中才有的,那么返回类型也要另外加上作用域运算符。

类内不能重新定义已经被类内使用过的类外定义的类型。所以,类内对于类型的定义,为避免出错,总是放在类的最开始位置。

如果成员是const,引用,或者某种为提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初始值。

初始化顺序与他们在类中定义的顺序一致,与在列表中的位置无关。

如果有默认的实参,那么也可能作为默认构造函数。

委托构造函数:把自己的一些职责委托给其他构造函数完成。

如CA():CA(“”,0,0){ }

转换构造函数:只有一个实参的构造函数,定义了从参数类型向类类型隐式转换的规则。

编译器只会自动的执行一步类型转换。(如string),但是内置类型的转换除外(可以多步)

对于有一个实参的构造函数,要抑制隐式类型转换,需加上explicit

聚合类:没有类内初始值,没有构造函数,没有基类和虚函数。都是public

更像最开始的结构体类型。

构造函数不能被声明成const的,直到构造函数完成初始化过程,才真正完成“常量”属性。

字面值常量类:虽然构造函数不能是const的,但是字面值常量类必须有个constexpr构造函数;

基于构造函数不能有返回值,而constexpr函数必须有且只有一条返回值。

那么一般我们用(必须初始化所有数据成员,构造函数列表初始化)

1:=default 使用默认的

2:=delete 只声明不能使用

3:函数体定义成空。

静态成员属于类,不属于对象。初始化必须在外部(内部现在也可以),私有静态函数初始化也没事。

如:

classCA

{

public:

staticstring name;

private:

staticstring setName()

{

return"wanggang";

}

};

stringCA::name= setName();

因为CA::之后就指明后面的都是CA作用域内的。

静态成员函数不包含this指针,而常成员函数的const是用来修饰this的,所以静态成员函数不能声明成const。

静态成员函数不能访问普通对象和函数,编译不过:“非静态成员引用必须与特定对象相对”;

1:静态成员的类型可以是他所属的类类型(不完全类型),而非静态成员只能是引用或指针。

2:静态数据成员可以作为默认实参。

class CA

{

public:

void fun(int a = sia);

static int sia;

}

常成员函数const声明和定义都要有。

=default在内部就是内联的,在外部就不是内联的。(只能声明,不能定义),不能内外都有(提示已有主体)

外部定义静态成员时,不能重复static关键字。(此处不能制定存储类)

const static 成员可以在类内初始化,类内和类外,只能初始化一个。(否则就是多次初始化)

inline内外都可以有。

virtual只能在类内的声明处。类外定义处不能有。

noexcept声明和定义,要么都有,要么都没有。需要在const/引用限定符之后,final,override和虚函数=0之前。

时间: 2024-10-12 23:58:47

C++primer知识点(一)的相关文章

C++primer知识点(二)

十六:IO操作 IO对象无拷贝和赋值,只能引用传递,并且不能是const 函数good() 所有错误位都没置位的情况下返回true; fail() 一般用作流使用的条件(failbit,badbit) eof()和bad()只表示特定的错误. 例如:将failbit和badbit复位,但eofbit不变 cin.clear(cin.rdstate() &~cin.failbit & ~cin.badbit); endl 换行并刷新缓冲区. 要每次输出都刷新缓冲区,用unitbuf   co

C++primer知识点(四)

二十四:面向对象 (1)数据抽象:接口实现分离. 继承:定义相似的类型 多态:以统一的方式使用.([父类]引用或指针调用虚函数 实现多态[动态绑定]) 如果表达式也不是引用,也不是指针,则动态类型永远与静态类型一致. virtual 在基类函数中说明,子类的相同函数默认都是virtual 子类参数列表(const/引用符后)后加上override,显示的注明改写基类的虚函数(帮助编译器检查) (2)虚析构函数:使释放父类引用或指针对象时,即使实际是派生类的对象,也能正确的释放.析构函数的虚属性也

C++primer知识点(三)

二十二: 1:拷贝控制操作 拷贝构造函数,拷贝赋值运算符,移动构造函数,移动赋值运算符,析构函数. 这些,在类的数据成员都能默认构造,拷贝,复制,销毁时,编译器默认都会有合成的版本. (1)   拷贝构造函数: Foo(const Foo&); 第一个参数是自身类类型的引用,额外的参数都有默认值. 几种情况下会被隐式使用,所以,不能是explicit 默认拷贝构造函数,又叫合成拷贝构造函数,也会逐元素的拷贝数组的成员. 拷贝初始化是依靠拷贝构造函数和移动构造函数来完成的. 调用的情况: 0)初始

C++primer知识点(五)(终结C++primer)

二十七: (1)异常使我们能将问题的检测和解决分离开来. 当匹配不到catch时,将调用标准库函数terminate(当异常没有被捕获) 异常对象:编译器使用异常抛出表达式来对异常对象进行拷贝初始化,因此throw表达式必须拥有完全类型(只是声明如class A;不是完全类型),如果是类类型的话,相应的类必须含有一个可访问的析构函数和一个可访问的拷贝或移动构造函数. 静态类型决定了异常对象的类型.如果throw表达式解引用一个基类指针,而该指针实际指向派生类对象,则抛出的对象将被切掉一部分,只有

【第一部分】c++ primer 零散的知识点

一些零散,即时的知识点,待处理. 上面的部分是问题,下面的部分是回答 一些重要概念: 一种类型不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算 p3 c++ 每个数据项都有其类型 p14 c++ 程序中的每个名字都要先声明后使用 p14 c++ 最初的一个设计焦点就是能定义使用上像内置类型一样自然的类类型 p17 程序应该尽量避免依赖于实现环境的行为 p33 对象是具有某种数据类型的内存空间 p39 初始化不是赋值 p39 定义于任何函数体之外的变量被初始化为0,定义在函数体内部的内置

C++ primer 第一部分C++基础(1-7章) 不熟悉的知识点 小结

P58 constexpr 和常量表达式 常量表达式 是指值不会改变 并且 在编译过程 就能得到计算结果的表达式; 字面值属于常量表达式; 用常量表示式初始化的const对象 也是常量表达式; constexpr变量 C++11:允许将变量声明为constexpr类型以便 由编译器来验证变量的值是否是一个常量表达式; 一般来说,如果你认定变量是一个常量表达式,那就把它声明成constexpr类型; P214 constexpr函数:是指能用于常量表达式的函数 函数的返回值类型及所有形参的类型都得

C Primer Plus 读后感

<C Primer Plus>中文版 第六版 从网站上搜索到这本书适合初学者而且是自学者可以看的C语言书籍,于是上网买了一本. 真是物有所值,通过本书我很系统的学习了一遍C语言,书中代码很少有错误(有些错误是因为编辑器不支持). 本书中的知识点很全面而且详细,阅读本书的同时最好要将代码运行一遍,对于有疑问的代码要多思考,一定要搞透彻. 本书中每一段代码之后,都有对代码的解析.所以看代码一定要先自己独立思考这段代码运行之后的结果是什么, 带着问题再去看代码之后的解析.如果问题还是没有明白,可以自

C++Primer第5版学习笔记(三)

C++Primer第5版学习笔记(三) 第四/五章的重难点内容 你可以点击这里回顾第三章内容 因为第五章的内容比较少,因此和第四章的笔记内容合并.   第四章是和表达式有关的知识,表达式是C++的基础设施,本章由三部分组成:         1.表达式概念基础,包括表达式的基本概念,左值和右值的概念,优先级结合律,求值顺序.  2.各种运算符,主要包括算数\关系\逻辑\赋值\递增递减\成员访问\条件\位运算\sizeof\逗号运算符 这10种运算符.  3.类型转换,包括隐式和显式两种转换的规则

《C++primer》v5 第1章 开始 读书笔记 习题答案

从今天开始在博客里写C++primer的文字.主要以后面的习题作业为主,会有必要的知识点补充. 本人也是菜鸟,可能有不对之处,还望指出. 前期内容可能会比较水. 1.1略 1.2略 1.3 cin和cout分别是istream和ostream的对象. #include<iostream> using namespace std; int main() { cout<<"Hello,world"<<endl; return 0; } 1.4 #incl