Error[8]: Undefined offset: 1548, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

char类型

C++对字符使用单引号,对字符串使用双引号。


字符用单引号括起,表示字符的数值编码。


int main(void) {
    using namespace std;
    char ch = 'M';       // 将 M 的 ASCII 码分配给 ch
    int i = ch;          // 将相同的代码存储在 int 中
    cout << "The ASCII code for " << ch << " is " << i << endl;
    cout << "Add one to the character code:" << endl;
    ch = ch + 1;          // 更改 ch 中的字符代码
    i = ch;               // 在 i 中保存新的字符代码
    //输出时,cout将值78转换为所显示的字符N;
    cout << "The ASCII code for " << ch << " is " << i << endl;
    // using the cout.put() member function to display a char
    cout << "Displaying char ch using cout.put(ch): ";
    cout.put(ch);
    // using cout.put() to display a char constant
    cout.put('!');
    cout << endl << "Done" << endl;
    return 0;
}
/*输出结果
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done
*/
数组

声明数组的通用格式:typeName arrayName[arraySize]
注意:数组最后一个元素的索引比数组长度小1.如下图

初始化方式:

int cards[4] = {3,4,5,6,78};
int cards[4] {3,4,5,6,78}; //c++ 11
int cards[4];

上面几种方式都可以。


C-风格字符串

C-风格字符串具有一种特殊的性质: 以空字符 (null character) 结尾,空字符被写作\0, 其 ASCII 码为0, 用来标记字符串的结尾。


char dog[8]={ 'b', 'e', '矿, 'U I I '文, I', 'I', 'I'};// not a string
char cat[8]={'f'' 'a', 't'' 'e'' 's'' 's', 'a','}'; //a stringchar
字符串常量(字符串字面值)
[ bird]ll= "Mr. Cheeps" ;char 
[ fish]= "Bubbles" ;int 

在确定存储宇符串所需的最短数组时, 别忘了将结尾的空宇符计算在内。



数组中使用字符串
main (void)using {
    namespace ; stdconst
    int = Size 15 ;char
    [ name1]Size;// 空数组               char
    [ name2]Size= "C++owboy" ;// 初始化数组  <<
    cout "Howdy! I'm " << ; name2<<
    cout "! What's your name?\n" ;;
    cin >> name1<<
    cout "Well, " << << name1 ", your name has " ;//另外, strlen()只计算可见的字符, 而不把空字符计算在内.
    //strlen返回的是存储在数组中的字符串的长度, 而不是数组本身的长度。


<< cout strlen ()name1<< " letters and is stored\n" ;//计算大小 << cout "in an array of " << sizeof ()name1<< " bytes.\n" ;<< cout "Your initial is " << [ name10]<< ".\n" ;[ name23]= ';' // set to null character<< "Here are the first 3 characters of my name: " cout ; <<<< cout ; name2 return endl0 ; }/*打印 Howdy! I'm C++owboy! What's your name? kim Well, kim, your name has 3 letters and is stored in an array of 15 bytes. Your initial is k. Here are the first 3 characters of my name: C++ */ int *

上面程序使用‘\0’截断了数组,具体原理如下图:

使用new创建动态数组
= new psome int [ 10];//创建一个包含10个int元素的数组//new运算符返回第一个元素的地址
//注意:使用new创建的数组,需要delete[]psome
int
main

怎么访问数组的元素?
只要把指针当作数组名使用(c++将数组名解释为地址)即可。


对于第一个元素,可以使用psome[0],第二个元素使用psome[1],依此类推。



指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。



例如:

将整数变量加1后,其值将增加1;但将指针变量加1后, 增加的量等千它指向的类型
的字节数。


将指向double的指针加1后,如果系统对double使用 8个字节存储, 则数值将增加 8;将指向
short的指针加1后, 如果系统对sho rt 使用2 个字节存储, 则指针值将增加2。


( void)usingnamespace {
    ; double std[
    3 wages]=10000.0 , {20000.0, 30000.0} ;short[
    3 stacks]=3 , {2, 1} ;// 这里有两种获取数组地址的方法double
    *
    = ;pw // 数组名 = 地址 wagesshort     *
    = &ps [ 0stacks];// 或使用地址运算符//带数组元素 <<
    "pw = "
    cout << << ", *pw = " pw << * << ;pw = endl+
    pw 1 pw ; <<"add 1 to the pw pointer:\n"
    cout ; <<"pw = "
    cout << << ", *pw = " pw << * << "\n\n"pw ; <<"ps = "
    cout << << ", *ps = " ps << * << ;ps = endl+
    ps 1 ps ; <<"add 1 to the ps pointer:\n"
    cout ; <<"ps = "
    cout << << ", *ps = " ps << * << "\n\n"ps ; <<"access two elements with array notation\n"
    cout ; <<"stacks[0] = "
    cout << [ 0 stacks]<<", stacks[1] = "
         << [ 1 stacks]<<; << endl"access two elements with pointer notation\n"
    cout ; <<"*stacks = "
    cout << * << ", *(stacks + 1) =  "stacks
         << * ( +1stacks ) <<; << endlsizeof
    cout ( )<<wages" = size of wages array\n" ; <<sizeof
    cout ( )<<pw" = size of pw pointer\n" ; return0
    ; }/*output
pw = 0x71601ff880, *pw = 10000
add 1 to the pw pointer:
pw = 0x71601ff888, *pw = 20000

ps = 0x71601ff87a, *ps = 3
add 1 to the ps pointer:
ps = 0x71601ff87c, *ps = 2

access two elements with array notation
stacks[0] = 3, stacks[1] = 2
access two elements with pointer notation
*stacks = 3, *(stacks + 1) =  2
24 = size of wages array
8 = size of pw pointer
*/
int
main

指针和字符串

直接看个例子:

( void)usingnamespace {
    ; char std[
    20 animal]="bear" ; //将char指针初始化为指向一个字符串,“wren”实际表示的是字符串的地址//因此这条语句将“wren”的地址赋值给了bird指针
    const
    char
    * = "wren"bird ; char*
    ; // 未初始化ps<<                  <<

    cout " and " animal ; <<<<
    cout "\n" bird ; // cout << ps << "\n";      //may display garbage, may cause a crash<<
    "Enter a kind of animal: "

    cout ; ;// ok if input < 20 chars
    cin >> animal//            point to allocated space              //这两个指针指向用一块内存和字符串
    // cin >> ps; Too horrible a blunder to try; ps doesn't
    =
    ;
    ps << animal<<                // 注意将animal赋给ps并不会复制字符串,只是复制地址。


cout "!\n" ps ; // ok, same as using animal<< "Before using strcpy():\n" cout ; <<<< //因为是char*类型。


所以需要强转为指针的地址类型 cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps //创建新的内存空间 endl= new ps char [ strlen()+animal1 ] ;// get new storage//第一个参数是目标地址,第二个参数是要复制字符串的地址 strcpy ( ,)ps; animal// 将字符串复制到新存储<< "After using strcpy():\n" cout ; <<<< cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps delete endl[ ];// cin.get(); ps// cin.get(); return 0 ; }/*output bear and wren Enter a kind of animal: dog dog! Before using strcpy(): dog at 0xad4c9ffd40 dog at 0xad4c9ffd40 After using strcpy(): dog at 0xad4c9ffd40 dog at 0x28c890018d0 */ for (

C-风格字符串比较

C++将C-风格字符串视为地址,所以使用关系运算符比较,无法得到满意的结果。



需要使用strcmp()函数来比较。



1.该函数接受两个字符串地址作为参数,这意味着参数可以是指针,字符串常量或者是字符数组名。



2.如果第一个字符串按字母顺序排在第二个字符串之前, 则strcmp()将返回一个负数值;
3.如果第一个字符串按字母顺序排在第 二个字符串之后, 则strcpm()将返回一个正数值。


使用关系运算符可以比较字符,因为字符实际上是整数。


例如:

= 'a'ch ; <='z' ch ; ++) ch<<;
	cout int chmain

例如,比较单词:

( void)usingnamespace {
    ; char std[
    5 word]="?ate" ; for(
    char ='a' ch ; strcmp( ,"mate"word) ;++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is 11"
    cout << << ; word return endl0
    ; }/*
?ate
aate
bate
cate
date
eate
fate
gate
hate
iate
jate
kate
late
After loop ends, word is 11mate
*/
int
main

7.比较string类字符串

( )usingnamespace {
    ; = std"?ate"
    string word ; for(

    char ='a' ch ; !="mate" word ; ++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is "
    cout << << ; word // cin.get(); endlreturn
    0
    ; }char
*

因为string类重载了!= 所以可以这样比较。


函数使用指针处理数组

1.C++将数组名解释其第一个元素的地址:
cookies == &cookies[0];
2.如果将取地址符&用于数组名时,将返回整个数组的地址。



3.在c++中,当且仅当用于函数头或函数原型中,int * arr和int arr [] 的含义才是相同的。


都意味着arr是一个int指针。



4.数组表示法 int arr []提醒用户,arr不仅指向int,还指向int数组的第一个int

请记住这个恒等式:

arr[i] == *(ar + i)
&arr[i] == ar + i
将指针(包括数组名) 加1’实际上是加上了一个与指针指向的类型的长度(以字节为单位)
相等的值。


对于遍历数组而言, 使用指针加法和数组下标时等效的。


buildstr (char,int c) ; n// prototypeint     main
( )usingnamespace {
    ; int std;
    char times;
    << ch"Enter a character: "

    cout ; ;<<
    cin >> ch"Enter an integer: "
    cout ; ;char
    cin >> times*
    = buildstrps ( ,)ch; times<<<<
    cout ; ps delete endl[
    ];// free memory ps=                   buildstr
    ps ( '+',20) ;// reuse pointer<<         <<
    cout "-DONE-" ps << << ; ps delete endl[
    ];// free memory ps// cin.get();                   // cin.get();
    return
    0
    ; }// builds string made of n c characters
char

*
buildstr (char,int c) char n* {
    = newpstr char [ +1n ] ;[]
    pstr=n';' // terminate string while(         --
    0 )n[ > ]=
        pstr;n// fill rest of string return c;        }
    /*output
Enter a character: z
Enter an integer: 10
zzzzzzzzzz
++++++++++++++++++++-DONE-++++++++++++++++++++
*/ pstr声明应像函数原型那样指出有关函数的信息
double
pam
函数指针基础使用

函数也有地址。


函数的地址是存储其机器语言代码的内存的开始地址。



可以编写将另一个函数的地址作为参数的函数。


这样第一个函数将能够找到第二个函数, 并运行它。


与直接调用另一个函数相比,这种方法很笨拙, 但它允许在不同的时间传递不同函数的地址, 这意味着可以在不同的时间使用不同的函数。


1.获取函数的地址
使用函数名即可。


(后面没有参数)例如:think()是一个函数,则think就是该函数的地址。



2.声明函数指针
我们在声明指向某种数据类型的指针时,必须指定指针指向的类型。



声明指向函数的指针时,也必须指定指针指向的函数类型。


这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。


也就是说, (


int );double(*
) (intpf);=;//注意, pam()的特征标和返回类型必须与pf 相同//通常,要声明指向特定类型的函数的指针, 可以首先编写这种函数的原型, 然后用(*pf)替换 函数名。


这样pf就是这类函数的指针。


pf double pam= pam ( x 4 );double=( * y ) (5pf);//-------------------------------double= pf ( y2 5 );#defineFLOAT_POINTER //上面使用pf和(*pf)等价。


//一种认为pf时函数指针,而*pf是函数,因此将(*pf)()用作函数调用。


//一种认为函数名是指向该函数的指针,指向函数的指针的行为应该和函数名相似。


怎么理解?
为了提供正确的运算符优先级,必须在声明中使用括号将pf括起。



括号的优先级比 * 运算符高,因此
pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf 是一个指向函数的指针。


typedef关键字

C++为类型建立别名的方式有两种。



一种是预处理器:
#define BYTE char// preprocessor replaces BYTE with char
这样, 预处理器将在编译程序时用 char 替换所有的 BYTE,从而使 BYTE 成为 char 的别名。


笫二种方法是使用 C++(和 C) 的关键字 typedef 来创建别名。



例如要将 byte 作为 char 的别名,可以这样做:
typedef char byte; / / makes byte an alias for char
下面是通用格式:
typedef typeName aliasName;
换句话说, 如果要将 aliasName 作为某种类型的别名, 可以声明 aliasName, 如同将 aliasName 声明为 这种类型的变量那样, 然后在声明的前面加上关键宇 typedef。


例如,要让 byte_pointer 成为 char 指针的别名, 可将 byte_pointer 声明为 char 指针, 然后在前面加上 typedef:
typedef char* byte_pointer;// pointer to char type
注意:某些场景下使用#define,不适用。


例如:

float* , ; //只有pa是float指针变量,pb只是float类型
FLOAT_POINTER patypedefpbdoubleconst

上面代码使用typedef方法不会有这样的问题。


typedef不会创建新类型,而只是为已有的类型建立一个新的名称。


使用typedef简化函数指针

double * ( * )(constp_fundouble*, int );=;//p1 points to the f1() function
p_fun p1 const 	f1int=
虚函数的工作原理

使用virtual的原因是:希望同一个方法在派生类和基类中的行为不同。


其中有2种机制来实现:
1.在派生类中重新定义基类的方法
2.使用虚方法

通常, 编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员。


隐藏成员中保存了一个指向函数
地址数组的指针。


这种数组称为虚函数表 (virtual function table, vtbl)。


虚函数表中存储了为类对象进行声
明的虚函数的地址。


例如,
1.基类对象包含一个指针, 该指针指向基类中所有虚函数的地址表。



2.派生类对象将包含一个指向独立地址表的指针。



3.如果派生类提供了虚函数的新定义, 该虚函数表将保存新函数的地址;
4.如果派生类没有重新定义虚函数, 该 vtbl 将保存函数原始版本的地址。



5.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。






上图解释:
对象A有两个虚函数vfunc1() 和vfunc2(),对象B复写了vfunc1(),并且有自己的func2() 对象C 也覆写了vfunc1()。



总结一下:
1 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生 出来的类) 中是虚的。



2 如果使用指向对象的引用或指针来调用虚方法, 程序将使用为对象类型定义的方法, 而不使用为引用或指针类型定义的方法。


这称为动态联编或晚期联编。


这种行为非常重要, 因为这样基类指 针或引用可以指向派生类对象。



3 如果定义的类将被用作基类, 则应将那些要在派生类中重新定义的类方法声明为虚的。



4 如果派生类没有重新定义函数, 将使用该函数的基类版本。


如果派生类位于派生链中, 则将使用最新的虚函数版本, 例外的情况是基类版本是隐藏的。


const使用


decltype

decltype(f())sum = x;//sum的类型就是函数f的返回类型
它的作用是选择并返回 *** 作数的数据类型。


在这个过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。



需要注意以下几点:
1.如果decltype使用的表达式是一个变量,则decltype返回该变量的类型。


(包括顶层const和引用在内。


0 , ci& =; decltypecj ( Cl)
=0ci;x //x的类型是const int decltype()
=;cj//y的类型是const int&,y绑定到变量xy decltypex()
;//错误z是一个引用,必须初始化cjintz=42

2.如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。



3.有些表达式将向decltype返回一个引用类型。


一般来说,意味着该表达式的结果对象能作为一条赋值语句的左值。


int i* =&; p  int &i=
; decltyper ( i+
//r是一个引用,因此decltype(r)的结果是引用类型。


r+0是一个具体的值 0)r;//加法的结果是int 因此b是一个为初始化的int.decltypeb(* //表达式的 *** 作时解引用,则decltype将得到引用类型。


);//错误的!!!p#cinclude#

解引用指针可以得到指针所指的对象。


而且还能给这个对象赋值。


4.decltype ((variable))(注意是双层括号)的结果永远是引用,而 decltype(variable)结果只有当variable本身就是一个引用时才是引用。


includestruct 
Adouble 
 
; } { ; xconst *;
decltype A( a)
 
;// y 的类型是 double(其声明类型)a->xdecltype y(       (
))=a->x;// z 的类型是 const double&(左值表达式) z template y< typename
 
T,typename Uauto add (>
, )decltypeT t( U u+ -> )// 返回类型依赖于模板形参t // C++14 开始可以推导返回类型 ureturn +
{                                     ;
    } tintumain
(
 
) int=33 
{
    ; i decltype ()
    =*i2 j ; i :: <<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j :: <<"i 和 j 的类型相同吗?"
 
    std<<cout ( ::
              < decltypestd(is_same_v),decltypei() ?"相同"j:> "不同" ) << '\n'; auto =[
 
    ] f ( int,int) aint return b* -> ;
    {
        } a ; bdecltype
    ()
 
    =;f// lambda 的类型是独有且无名的 g = ff (
    i 2 ,2); =g(
    j 3 ,3); ::<<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j } /*output
i = 33, j = 66
i 和 j 的类型相同吗?相同
i = 4, j = 9
*/void
f
(
模板别名using

用法:
对命名空间的 using 指令及对命名空间成员的 using 声明.

) ;namespacevoidg
 
( A
{
    ) ;}namespaceusing
::
 
; X
{
    // 全局 f 现在作为 ::X::f 可见 usingf::        ;
    // A::g 现在作为 ::X::g 可见 Ausingg::       ,
    :: A;g//(C++17)OK:命名空间作用域允许双重声明 A}gvoid h
(
 
) X::f
{
    ();// 调用 ::fX:: g
    ();// 调用 A::g}# include
// 基类 B

对类成员的 using 声明.

structB 
 
virtual
void f
{
    ( int )::<<"B::f\n" { std;cout } voidg (
    char )::<<"B::g(char)\n"        { std;cout } voidh (
    int )::<<"B::h\n"         { std;cout } protected: int
;// B::m 是受保护的
    typedef mint ;
    } ; value_type// 派生类 D
structD
 
:
B using :: ;
{
    // D::m 是公开的 Busingm:: ;
    // D::value_type 是公开的 B// using 的非必要使用场合:value_type// B::f 已经公开,它作为 D::f 公开可用, using
 
    ::
    ;
    // 并且下面的 D::f 和上面的 B::f 有相同的签名。


void Bff( int )::<<"D::f\n" { std;cout } // D::f(int) **覆盖** B::f(int)// using 的必要使用场合: // B::g(char) 被 D::g(int) 隐藏, // 如果不用 using B::g,将 char 传递给 D::g(char) 实际上调用的是下面定义的 D::g(int), using :: // 除非它在这里通过 using B::g 显式暴露为 D::g(char)。


; // 因为后者隐藏了 B::g(char),并且 char 形参会因此隐式转型到 int。


// 将 B::g(char) 作为 D::g(char) 暴露, Bvoidgg ( // 后者与下面定义的 D::g(int) 完全是不同的函数。


int )::<<"D::g(int)\n" { std;cout } // g(int) 与 g(char) 均作为 D 的成员可见// using 的非必要使用场合: // B::h 已经公开,它作为 D::h 公开可用, using :: ; // 并且下面的 D::h 和上面的 B::h 有相同的签名。


void Bhh( int )::<<"D::h\n" { std;cout } // D::h(int) **隐藏** B::h(int)} ; int main( ) ;&= { D d; B// b.m = 2; // 错误:B::m 受保护 b . d= 1 d;m // 受保护的 B::m 可以作为公开的 D::m 访问 .f ( b1);// 调用派生类的 f().f ( d1);// 调用派生类的 f()::<< "----------\n" std;cout . g( d1);// 调用派生类的 g(int).g ( d'a');// 调用基类的 g(char),它**只是因为**// 派生类中用到了 using B::g; 才会暴露:: << "----------\n" std;cout . h( b1);// 调用基类的 h().h ( d1);// 调用派生类的 h()}/*output D::f D::f ---------- D::g(int) B::g(char) ---------- B::h D::h */ # include #

类型别名与别名模板声明 (C++11 起).

include# 
include// 类型别名,等同于 
// typedef std::ios_base::fmtflags flags;using 
 
=
::
:: flags ; std// 名字 'flags' 现在指代类型:ios_base=fmtflags::
::
flags fl ; std// 类型别名,等同于ios_base// typedef void (*func)(int, int);decusing
 
=
void
( func * ) (int, int); // 名字 'func' 现在指代函数指针:voidexample
(
int ,int)} =; {// 别名模板
func f template example<
 
class
Tusing= *>
; ptr < Tint; 
// 名字 'ptr' 现在是指向 T 的指针的别名
ptr// 用于隐藏模板形参的别名模版template> x<
 
class
CharTusing= ::>
< mystring , std::basic_string<CharT; std<char_traitscharCharT>>;
mystring// 别名模板可引入成员 typedef 名template> str<
 
typename
TstructContainer using>
= ; { } value_type ; T// 可用于泛型编程 template<
typename
ContainerTypevoidg (>
const &)typename ContainerTypeContainerType c:: { ; }// 用于简化 std::enable_if 语法的类型别名value_type ntemplate <
 
typename
Tusing= typename>
T Invoke :: ; template<typetypename
Conditionusing= <>
:: EnableIf < Invoke::std;enable_iftemplateCondition<value>>typename
T,typename =< :: < EnableIfintstdfpoly_onlyis_polymorphic(T>>>
) return1T t; { } structS virtual
 
~ S { ( )}}; {int main(
 
) <int; 
{
    Containerg(> c)
    ;// Container::value_type 将在此函数中是 intc//  fpoly_only(c); // 错误:被 enable_if 禁止; fpoly_only
(
    S s)
    ;// OK:被 enable_if 允许s}struct A
A
explicit关键字

指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。



声明时不带函数说明符 explicit 的拥有单个无默认值形参的 (C++11 前)构造函数被称作转换构造函数。


( int
{
    )}// 转换构造函数A { (      int
    ,int)} // 转换构造函数(C++11)operator { bool (
    ) constreturntrue ; { } }; struct
Bexplicit
 
B (
{
    int )}explicitB { (
    int ,int)} explicitoperator { bool
    ( ) constreturntrue ; { } }; int
main(
 
) =1;
{
    A a1 // OK:复制初始化选择 A::A(int) a2(      2
    A );// OK:直接初始化选择 A::A(int)4,       5
    A a3 {}; // OK:直接列表初始化选择 A::A(int, int)=4   ,
    A a4 5 {}; // OK:复制列表初始化选择 A::A(int, int)=( )
    A a5 1 ;A// OK:显式转型进行 static_castif(   )
    ; // OK:A::operator bool()a1bool =      ;
    // OK:复制初始化选择 A::operator bool() na1 bool a1= static_cast
    < na2 bool ();>// OK:static_cast 进行直接初始化a1//  B b1 = 1;      // 错误:复制初始化不考虑 B::B(int)b2 (
 
2
    B );// OK:直接初始化选择 B::B(int)4,       5
    B b3 {}; // OK:直接列表初始化选择 B::B(int, int)//  B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)=   (
)
    B b5 1 ;B// OK:显式转型进行 static_castif(   )
    ; // OK:B::operator bool()b2//  bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool() bool      =
static_cast
    < nb2 bool ();>// OK:static_cast 进行直接初始化b2}[+++] [+++]
[+++]
)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1549, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

char类型

C++对字符使用单引号,对字符串使用双引号。


字符用单引号括起,表示字符的数值编码。


int main(void) {
    using namespace std;
    char ch = 'M';       // 将 M 的 ASCII 码分配给 ch
    int i = ch;          // 将相同的代码存储在 int 中
    cout << "The ASCII code for " << ch << " is " << i << endl;
    cout << "Add one to the character code:" << endl;
    ch = ch + 1;          // 更改 ch 中的字符代码
    i = ch;               // 在 i 中保存新的字符代码
    //输出时,cout将值78转换为所显示的字符N;
    cout << "The ASCII code for " << ch << " is " << i << endl;
    // using the cout.put() member function to display a char
    cout << "Displaying char ch using cout.put(ch): ";
    cout.put(ch);
    // using cout.put() to display a char constant
    cout.put('!');
    cout << endl << "Done" << endl;
    return 0;
}
/*输出结果
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done
*/
数组

声明数组的通用格式:typeName arrayName[arraySize]
注意:数组最后一个元素的索引比数组长度小1.如下图

初始化方式:

int cards[4] = {3,4,5,6,78};
int cards[4] {3,4,5,6,78}; //c++ 11
int cards[4];

上面几种方式都可以。


C-风格字符串

C-风格字符串具有一种特殊的性质: 以空字符 (null character) 结尾,空字符被写作\0, 其 ASCII 码为0, 用来标记字符串的结尾。


char dog[8]={ 'b', 'e', '矿, 'U I I '文, I', 'I', 'I'};// not a string
char cat[8]={'f'' 'a', 't'' 'e'' 's'' 's', 'a','}'; //a stringchar
字符串常量(字符串字面值)
[ bird]ll= "Mr. Cheeps" ;char 
[ fish]= "Bubbles" ;int 

在确定存储宇符串所需的最短数组时, 别忘了将结尾的空宇符计算在内。



数组中使用字符串
main (void)using {
    namespace ; stdconst
    int = Size 15 ;char
    [ name1]Size;// 空数组               char
    [ name2]Size= "C++owboy" ;// 初始化数组  <<
    cout "Howdy! I'm " << ; name2<<
    cout "! What's your name?\n" ;;
    cin >> name1<<
    cout "Well, " << << name1 ", your name has " ;//另外, strlen()只计算可见的字符, 而不把空字符计算在内.
    //strlen返回的是存储在数组中的字符串的长度, 而不是数组本身的长度。


<< cout strlen ()name1<< " letters and is stored\n" ;//计算大小 << cout "in an array of " << sizeof ()name1<< " bytes.\n" ;<< cout "Your initial is " << [ name10]<< ".\n" ;[ name23]= ';' // set to null character<< "Here are the first 3 characters of my name: " cout ; <<<< cout ; name2 return endl0 ; }/*打印 Howdy! I'm C++owboy! What's your name? kim Well, kim, your name has 3 letters and is stored in an array of 15 bytes. Your initial is k. Here are the first 3 characters of my name: C++ */ int *

上面程序使用‘\0’截断了数组,具体原理如下图:

使用new创建动态数组
= new psome int [ 10];//创建一个包含10个int元素的数组//new运算符返回第一个元素的地址
//注意:使用new创建的数组,需要delete[]psome
int
main

怎么访问数组的元素?
只要把指针当作数组名使用(c++将数组名解释为地址)即可。


对于第一个元素,可以使用psome[0],第二个元素使用psome[1],依此类推。



指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。



例如:

将整数变量加1后,其值将增加1;但将指针变量加1后, 增加的量等千它指向的类型
的字节数。


将指向double的指针加1后,如果系统对double使用 8个字节存储, 则数值将增加 8;将指向
short的指针加1后, 如果系统对sho rt 使用2 个字节存储, 则指针值将增加2。


( void)usingnamespace {
    ; double std[
    3 wages]=10000.0 , {20000.0, 30000.0} ;short[
    3 stacks]=3 , {2, 1} ;// 这里有两种获取数组地址的方法double
    *
    = ;pw // 数组名 = 地址 wagesshort     *
    = &ps [ 0stacks];// 或使用地址运算符//带数组元素 <<
    "pw = "
    cout << << ", *pw = " pw << * << ;pw = endl+
    pw 1 pw ; <<"add 1 to the pw pointer:\n"
    cout ; <<"pw = "
    cout << << ", *pw = " pw << * << "\n\n"pw ; <<"ps = "
    cout << << ", *ps = " ps << * << ;ps = endl+
    ps 1 ps ; <<"add 1 to the ps pointer:\n"
    cout ; <<"ps = "
    cout << << ", *ps = " ps << * << "\n\n"ps ; <<"access two elements with array notation\n"
    cout ; <<"stacks[0] = "
    cout << [ 0 stacks]<<", stacks[1] = "
         << [ 1 stacks]<<; << endl"access two elements with pointer notation\n"
    cout ; <<"*stacks = "
    cout << * << ", *(stacks + 1) =  "stacks
         << * ( +1stacks ) <<; << endlsizeof
    cout ( )<<wages" = size of wages array\n" ; <<sizeof
    cout ( )<<pw" = size of pw pointer\n" ; return0
    ; }/*output
pw = 0x71601ff880, *pw = 10000
add 1 to the pw pointer:
pw = 0x71601ff888, *pw = 20000

ps = 0x71601ff87a, *ps = 3
add 1 to the ps pointer:
ps = 0x71601ff87c, *ps = 2

access two elements with array notation
stacks[0] = 3, stacks[1] = 2
access two elements with pointer notation
*stacks = 3, *(stacks + 1) =  2
24 = size of wages array
8 = size of pw pointer
*/
int
main

指针和字符串

直接看个例子:

( void)usingnamespace {
    ; char std[
    20 animal]="bear" ; //将char指针初始化为指向一个字符串,“wren”实际表示的是字符串的地址//因此这条语句将“wren”的地址赋值给了bird指针
    const
    char
    * = "wren"bird ; char*
    ; // 未初始化ps<<                  <<

    cout " and " animal ; <<<<
    cout "\n" bird ; // cout << ps << "\n";      //may display garbage, may cause a crash<<
    "Enter a kind of animal: "

    cout ; ;// ok if input < 20 chars
    cin >> animal//            point to allocated space              //这两个指针指向用一块内存和字符串
    // cin >> ps; Too horrible a blunder to try; ps doesn't
    =
    ;
    ps << animal<<                // 注意将animal赋给ps并不会复制字符串,只是复制地址。


cout "!\n" ps ; // ok, same as using animal<< "Before using strcpy():\n" cout ; <<<< //因为是char*类型。


所以需要强转为指针的地址类型 cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps //创建新的内存空间 endl= new ps char [ strlen()+animal1 ] ;// get new storage//第一个参数是目标地址,第二个参数是要复制字符串的地址 strcpy ( ,)ps; animal// 将字符串复制到新存储<< "After using strcpy():\n" cout ; <<<< cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps delete endl[ ];// cin.get(); ps// cin.get(); return 0 ; }/*output bear and wren Enter a kind of animal: dog dog! Before using strcpy(): dog at 0xad4c9ffd40 dog at 0xad4c9ffd40 After using strcpy(): dog at 0xad4c9ffd40 dog at 0x28c890018d0 */ for (

C-风格字符串比较

C++将C-风格字符串视为地址,所以使用关系运算符比较,无法得到满意的结果。



需要使用strcmp()函数来比较。



1.该函数接受两个字符串地址作为参数,这意味着参数可以是指针,字符串常量或者是字符数组名。



2.如果第一个字符串按字母顺序排在第二个字符串之前, 则strcmp()将返回一个负数值;
3.如果第一个字符串按字母顺序排在第 二个字符串之后, 则strcpm()将返回一个正数值。


使用关系运算符可以比较字符,因为字符实际上是整数。


例如:

= 'a'ch ; <='z' ch ; ++) ch<<;
	cout int chmain

例如,比较单词:

( void)usingnamespace {
    ; char std[
    5 word]="?ate" ; for(
    char ='a' ch ; strcmp( ,"mate"word) ;++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is 11"
    cout << << ; word return endl0
    ; }/*
?ate
aate
bate
cate
date
eate
fate
gate
hate
iate
jate
kate
late
After loop ends, word is 11mate
*/
int
main

7.比较string类字符串

( )usingnamespace {
    ; = std"?ate"
    string word ; for(

    char ='a' ch ; !="mate" word ; ++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is "
    cout << << ; word // cin.get(); endlreturn
    0
    ; }char
*

因为string类重载了!= 所以可以这样比较。


函数使用指针处理数组

1.C++将数组名解释其第一个元素的地址:
cookies == &cookies[0];
2.如果将取地址符&用于数组名时,将返回整个数组的地址。



3.在c++中,当且仅当用于函数头或函数原型中,int * arr和int arr [] 的含义才是相同的。


都意味着arr是一个int指针。



4.数组表示法 int arr []提醒用户,arr不仅指向int,还指向int数组的第一个int

请记住这个恒等式:

arr[i] == *(ar + i)
&arr[i] == ar + i
将指针(包括数组名) 加1’实际上是加上了一个与指针指向的类型的长度(以字节为单位)
相等的值。


对于遍历数组而言, 使用指针加法和数组下标时等效的。


buildstr (char,int c) ; n// prototypeint     main
( )usingnamespace {
    ; int std;
    char times;
    << ch"Enter a character: "

    cout ; ;<<
    cin >> ch"Enter an integer: "
    cout ; ;char
    cin >> times*
    = buildstrps ( ,)ch; times<<<<
    cout ; ps delete endl[
    ];// free memory ps=                   buildstr
    ps ( '+',20) ;// reuse pointer<<         <<
    cout "-DONE-" ps << << ; ps delete endl[
    ];// free memory ps// cin.get();                   // cin.get();
    return
    0
    ; }// builds string made of n c characters
char

*
buildstr (char,int c) char n* {
    = newpstr char [ +1n ] ;[]
    pstr=n';' // terminate string while(         --
    0 )n[ > ]=
        pstr;n// fill rest of string return c;        }
    /*output
Enter a character: z
Enter an integer: 10
zzzzzzzzzz
++++++++++++++++++++-DONE-++++++++++++++++++++
*/ pstr声明应像函数原型那样指出有关函数的信息
double
pam
函数指针基础使用

函数也有地址。


函数的地址是存储其机器语言代码的内存的开始地址。



可以编写将另一个函数的地址作为参数的函数。


这样第一个函数将能够找到第二个函数, 并运行它。


与直接调用另一个函数相比,这种方法很笨拙, 但它允许在不同的时间传递不同函数的地址, 这意味着可以在不同的时间使用不同的函数。


1.获取函数的地址
使用函数名即可。


(后面没有参数)例如:think()是一个函数,则think就是该函数的地址。



2.声明函数指针
我们在声明指向某种数据类型的指针时,必须指定指针指向的类型。



声明指向函数的指针时,也必须指定指针指向的函数类型。


这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。


也就是说, (


int );double(*
) (intpf);=;//注意, pam()的特征标和返回类型必须与pf 相同//通常,要声明指向特定类型的函数的指针, 可以首先编写这种函数的原型, 然后用(*pf)替换 函数名。


这样pf就是这类函数的指针。


pf double pam= pam ( x 4 );double=( * y ) (5pf);//-------------------------------double= pf ( y2 5 );#defineFLOAT_POINTER //上面使用pf和(*pf)等价。


//一种认为pf时函数指针,而*pf是函数,因此将(*pf)()用作函数调用。


//一种认为函数名是指向该函数的指针,指向函数的指针的行为应该和函数名相似。


怎么理解?
为了提供正确的运算符优先级,必须在声明中使用括号将pf括起。



括号的优先级比 * 运算符高,因此
pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf 是一个指向函数的指针。


typedef关键字

C++为类型建立别名的方式有两种。



一种是预处理器:
#define BYTE char// preprocessor replaces BYTE with char
这样, 预处理器将在编译程序时用 char 替换所有的 BYTE,从而使 BYTE 成为 char 的别名。


笫二种方法是使用 C++(和 C) 的关键字 typedef 来创建别名。



例如要将 byte 作为 char 的别名,可以这样做:
typedef char byte; / / makes byte an alias for char
下面是通用格式:
typedef typeName aliasName;
换句话说, 如果要将 aliasName 作为某种类型的别名, 可以声明 aliasName, 如同将 aliasName 声明为 这种类型的变量那样, 然后在声明的前面加上关键宇 typedef。


例如,要让 byte_pointer 成为 char 指针的别名, 可将 byte_pointer 声明为 char 指针, 然后在前面加上 typedef:
typedef char* byte_pointer;// pointer to char type
注意:某些场景下使用#define,不适用。


例如:

float* , ; //只有pa是float指针变量,pb只是float类型
FLOAT_POINTER patypedefpbdoubleconst

上面代码使用typedef方法不会有这样的问题。


typedef不会创建新类型,而只是为已有的类型建立一个新的名称。


使用typedef简化函数指针

double * ( * )(constp_fundouble*, int );=;//p1 points to the f1() function
p_fun p1 const 	f1int=
虚函数的工作原理

使用virtual的原因是:希望同一个方法在派生类和基类中的行为不同。


其中有2种机制来实现:
1.在派生类中重新定义基类的方法
2.使用虚方法

通常, 编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员。


隐藏成员中保存了一个指向函数
地址数组的指针。


这种数组称为虚函数表 (virtual function table, vtbl)。


虚函数表中存储了为类对象进行声
明的虚函数的地址。


例如,
1.基类对象包含一个指针, 该指针指向基类中所有虚函数的地址表。



2.派生类对象将包含一个指向独立地址表的指针。



3.如果派生类提供了虚函数的新定义, 该虚函数表将保存新函数的地址;
4.如果派生类没有重新定义虚函数, 该 vtbl 将保存函数原始版本的地址。



5.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。






上图解释:
对象A有两个虚函数vfunc1() 和vfunc2(),对象B复写了vfunc1(),并且有自己的func2() 对象C 也覆写了vfunc1()。



总结一下:
1 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生 出来的类) 中是虚的。



2 如果使用指向对象的引用或指针来调用虚方法, 程序将使用为对象类型定义的方法, 而不使用为引用或指针类型定义的方法。


这称为动态联编或晚期联编。


这种行为非常重要, 因为这样基类指 针或引用可以指向派生类对象。



3 如果定义的类将被用作基类, 则应将那些要在派生类中重新定义的类方法声明为虚的。



4 如果派生类没有重新定义函数, 将使用该函数的基类版本。


如果派生类位于派生链中, 则将使用最新的虚函数版本, 例外的情况是基类版本是隐藏的。


const使用


decltype

decltype(f())sum = x;//sum的类型就是函数f的返回类型
它的作用是选择并返回 *** 作数的数据类型。


在这个过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。



需要注意以下几点:
1.如果decltype使用的表达式是一个变量,则decltype返回该变量的类型。


(包括顶层const和引用在内。


0 , ci& =; decltypecj ( Cl)
=0ci;x //x的类型是const int decltype()
=;cj//y的类型是const int&,y绑定到变量xy decltypex()
;//错误z是一个引用,必须初始化cjintz=42

2.如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。



3.有些表达式将向decltype返回一个引用类型。


一般来说,意味着该表达式的结果对象能作为一条赋值语句的左值。


int i* =&; p  int &i=
; decltyper ( i+
//r是一个引用,因此decltype(r)的结果是引用类型。


r+0是一个具体的值 0)r;//加法的结果是int 因此b是一个为初始化的int.decltypeb(* //表达式的 *** 作时解引用,则decltype将得到引用类型。


);//错误的!!!p#cinclude#

解引用指针可以得到指针所指的对象。


而且还能给这个对象赋值。


4.decltype ((variable))(注意是双层括号)的结果永远是引用,而 decltype(variable)结果只有当variable本身就是一个引用时才是引用。


includestruct 
Adouble 
 
; } { ; xconst *;
decltype A( a)
 
;// y 的类型是 double(其声明类型)a->xdecltype y(       (
))=a->x;// z 的类型是 const double&(左值表达式) z template y< typename
 
T,typename Uauto add (>
, )decltypeT t( U u+ -> )// 返回类型依赖于模板形参t // C++14 开始可以推导返回类型 ureturn +
{                                     ;
    } tintumain
(
 
) int=33 
{
    ; i decltype ()
    =*i2 j ; i :: <<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j :: <<"i 和 j 的类型相同吗?"
 
    std<<cout ( ::
              < decltypestd(is_same_v),decltypei() ?"相同"j:> "不同" ) << '\n'; auto =[
 
    ] f ( int,int) aint return b* -> ;
    {
        } a ; bdecltype
    ()
 
    =;f// lambda 的类型是独有且无名的 g = ff (
    i 2 ,2); =g(
    j 3 ,3); ::<<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j } /*output
i = 33, j = 66
i 和 j 的类型相同吗?相同
i = 4, j = 9
*/void
f
(
模板别名using

用法:
对命名空间的 using 指令及对命名空间成员的 using 声明.

) ;namespacevoidg
 
( A
{
    ) ;}namespaceusing
::
 
; X
{
    // 全局 f 现在作为 ::X::f 可见 usingf::        ;
    // A::g 现在作为 ::X::g 可见 Ausingg::       ,
    :: A;g//(C++17)OK:命名空间作用域允许双重声明 A}gvoid h
(
 
) X::f
{
    ();// 调用 ::fX:: g
    ();// 调用 A::g}# include
// 基类 B

对类成员的 using 声明.

structB 
 
virtual
void f
{
    ( int )::<<"B::f\n" { std;cout } voidg (
    char )::<<"B::g(char)\n"        { std;cout } voidh (
    int )::<<"B::h\n"         { std;cout } protected: int
;// B::m 是受保护的
    typedef mint ;
    } ; value_type// 派生类 D
structD
 
:
B using :: ;
{
    // D::m 是公开的 Busingm:: ;
    // D::value_type 是公开的 B// using 的非必要使用场合:value_type// B::f 已经公开,它作为 D::f 公开可用, using
 
    ::
    ;
    // 并且下面的 D::f 和上面的 B::f 有相同的签名。


void Bff( int )::<<"D::f\n" { std;cout } // D::f(int) **覆盖** B::f(int)// using 的必要使用场合: // B::g(char) 被 D::g(int) 隐藏, // 如果不用 using B::g,将 char 传递给 D::g(char) 实际上调用的是下面定义的 D::g(int), using :: // 除非它在这里通过 using B::g 显式暴露为 D::g(char)。


; // 因为后者隐藏了 B::g(char),并且 char 形参会因此隐式转型到 int。


// 将 B::g(char) 作为 D::g(char) 暴露, Bvoidgg ( // 后者与下面定义的 D::g(int) 完全是不同的函数。


int )::<<"D::g(int)\n" { std;cout } // g(int) 与 g(char) 均作为 D 的成员可见// using 的非必要使用场合: // B::h 已经公开,它作为 D::h 公开可用, using :: ; // 并且下面的 D::h 和上面的 B::h 有相同的签名。


void Bhh( int )::<<"D::h\n" { std;cout } // D::h(int) **隐藏** B::h(int)} ; int main( ) ;&= { D d; B// b.m = 2; // 错误:B::m 受保护 b . d= 1 d;m // 受保护的 B::m 可以作为公开的 D::m 访问 .f ( b1);// 调用派生类的 f().f ( d1);// 调用派生类的 f()::<< "----------\n" std;cout . g( d1);// 调用派生类的 g(int).g ( d'a');// 调用基类的 g(char),它**只是因为**// 派生类中用到了 using B::g; 才会暴露:: << "----------\n" std;cout . h( b1);// 调用基类的 h().h ( d1);// 调用派生类的 h()}/*output D::f D::f ---------- D::g(int) B::g(char) ---------- B::h D::h */ # include #

类型别名与别名模板声明 (C++11 起).

include# 
include// 类型别名,等同于 
// typedef std::ios_base::fmtflags flags;using 
 
=
::
:: flags ; std// 名字 'flags' 现在指代类型:ios_base=fmtflags::
::
flags fl ; std// 类型别名,等同于ios_base// typedef void (*func)(int, int);decusing
 
=
void
( func * ) (int, int); // 名字 'func' 现在指代函数指针:voidexample
(
int ,int)} =; {// 别名模板
func f template example<
 
class
Tusing= *>
; ptr < Tint; 
// 名字 'ptr' 现在是指向 T 的指针的别名
ptr// 用于隐藏模板形参的别名模版template> x<
 
class
CharTusing= ::>
< mystring , std::basic_string<CharT; std<char_traitscharCharT>>;
mystring// 别名模板可引入成员 typedef 名template> str<
 
typename
TstructContainer using>
= ; { } value_type ; T// 可用于泛型编程 template<
typename
ContainerTypevoidg (>
const &)typename ContainerTypeContainerType c:: { ; }// 用于简化 std::enable_if 语法的类型别名value_type ntemplate <
 
typename
Tusing= typename>
T Invoke :: ; template<typetypename
Conditionusing= <>
:: EnableIf < Invoke::std;enable_iftemplateCondition<value>>typename
T,typename =< :: < EnableIfintstdfpoly_onlyis_polymorphic(T>>>
) return1T t; { } structS virtual
 
~ S { ( )}}; {int main(
 
) <int; 
{
    Containerg(> c)
    ;// Container::value_type 将在此函数中是 intc//  fpoly_only(c); // 错误:被 enable_if 禁止; fpoly_only
(
    S s)
    ;// OK:被 enable_if 允许s}struct A
A
explicit关键字

指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。



声明时不带函数说明符 explicit 的拥有单个无默认值形参的 (C++11 前)构造函数被称作转换构造函数。


( int
{
    )}// 转换构造函数A { (      int
    ,int)} // 转换构造函数(C++11)operator { bool (
    ) constreturntrue ; { } }; struct
Bexplicit
 
B (
{
    int )}explicitB { (
    int ,int)} explicitoperator { bool
    ( ) constreturntrue ; { } }; int
main(
 
) =1;
{
    A a1 // OK:复制初始化选择 A::A(int) a2(      2
    A );// OK:直接初始化选择 A::A(int)4,       5
    A a3 {}; // OK:直接列表初始化选择 A::A(int, int)=4   ,
    A a4 5 {}; // OK:复制列表初始化选择 A::A(int, int)=( )
    A a5 1 ;A// OK:显式转型进行 static_castif(   )
    ; // OK:A::operator bool()a1bool =      ;
    // OK:复制初始化选择 A::operator bool() na1 bool a1= static_cast
    < na2 bool ();>// OK:static_cast 进行直接初始化a1//  B b1 = 1;      // 错误:复制初始化不考虑 B::B(int)b2 (
 
2
    B );// OK:直接初始化选择 B::B(int)4,       5
    B b3 {}; // OK:直接列表初始化选择 B::B(int, int)//  B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)=   (
)
    B b5 1 ;B// OK:显式转型进行 static_castif(   )
    ; // OK:B::operator bool()b2//  bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool() bool      =
static_cast
    < nb2 bool ();>// OK:static_cast 进行直接初始化b2} [+++]
[+++]
)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 1550, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

char类型

C++对字符使用单引号,对字符串使用双引号。


字符用单引号括起,表示字符的数值编码。


int main(void) {
    using namespace std;
    char ch = 'M';       // 将 M 的 ASCII 码分配给 ch
    int i = ch;          // 将相同的代码存储在 int 中
    cout << "The ASCII code for " << ch << " is " << i << endl;
    cout << "Add one to the character code:" << endl;
    ch = ch + 1;          // 更改 ch 中的字符代码
    i = ch;               // 在 i 中保存新的字符代码
    //输出时,cout将值78转换为所显示的字符N;
    cout << "The ASCII code for " << ch << " is " << i << endl;
    // using the cout.put() member function to display a char
    cout << "Displaying char ch using cout.put(ch): ";
    cout.put(ch);
    // using cout.put() to display a char constant
    cout.put('!');
    cout << endl << "Done" << endl;
    return 0;
}
/*输出结果
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done
*/
数组

声明数组的通用格式:typeName arrayName[arraySize]
注意:数组最后一个元素的索引比数组长度小1.如下图

初始化方式:

int cards[4] = {3,4,5,6,78};
int cards[4] {3,4,5,6,78}; //c++ 11
int cards[4];

上面几种方式都可以。


C-风格字符串

C-风格字符串具有一种特殊的性质: 以空字符 (null character) 结尾,空字符被写作\0, 其 ASCII 码为0, 用来标记字符串的结尾。


char dog[8]={ 'b', 'e', '矿, 'U I I '文, I', 'I', 'I'};// not a string
char cat[8]={'f'' 'a', 't'' 'e'' 's'' 's', 'a','}'; //a stringchar
字符串常量(字符串字面值)
[ bird]ll= "Mr. Cheeps" ;char 
[ fish]= "Bubbles" ;int 

在确定存储宇符串所需的最短数组时, 别忘了将结尾的空宇符计算在内。



数组中使用字符串
main (void)using {
    namespace ; stdconst
    int = Size 15 ;char
    [ name1]Size;// 空数组               char
    [ name2]Size= "C++owboy" ;// 初始化数组  <<
    cout "Howdy! I'm " << ; name2<<
    cout "! What's your name?\n" ;;
    cin >> name1<<
    cout "Well, " << << name1 ", your name has " ;//另外, strlen()只计算可见的字符, 而不把空字符计算在内.
    //strlen返回的是存储在数组中的字符串的长度, 而不是数组本身的长度。


<< cout strlen ()name1<< " letters and is stored\n" ;//计算大小 << cout "in an array of " << sizeof ()name1<< " bytes.\n" ;<< cout "Your initial is " << [ name10]<< ".\n" ;[ name23]= ';' // set to null character<< "Here are the first 3 characters of my name: " cout ; <<<< cout ; name2 return endl0 ; }/*打印 Howdy! I'm C++owboy! What's your name? kim Well, kim, your name has 3 letters and is stored in an array of 15 bytes. Your initial is k. Here are the first 3 characters of my name: C++ */ int *

上面程序使用‘\0’截断了数组,具体原理如下图:

使用new创建动态数组
= new psome int [ 10];//创建一个包含10个int元素的数组//new运算符返回第一个元素的地址
//注意:使用new创建的数组,需要delete[]psome
int
main

怎么访问数组的元素?
只要把指针当作数组名使用(c++将数组名解释为地址)即可。


对于第一个元素,可以使用psome[0],第二个元素使用psome[1],依此类推。



指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。



例如:

将整数变量加1后,其值将增加1;但将指针变量加1后, 增加的量等千它指向的类型
的字节数。


将指向double的指针加1后,如果系统对double使用 8个字节存储, 则数值将增加 8;将指向
short的指针加1后, 如果系统对sho rt 使用2 个字节存储, 则指针值将增加2。


( void)usingnamespace {
    ; double std[
    3 wages]=10000.0 , {20000.0, 30000.0} ;short[
    3 stacks]=3 , {2, 1} ;// 这里有两种获取数组地址的方法double
    *
    = ;pw // 数组名 = 地址 wagesshort     *
    = &ps [ 0stacks];// 或使用地址运算符//带数组元素 <<
    "pw = "
    cout << << ", *pw = " pw << * << ;pw = endl+
    pw 1 pw ; <<"add 1 to the pw pointer:\n"
    cout ; <<"pw = "
    cout << << ", *pw = " pw << * << "\n\n"pw ; <<"ps = "
    cout << << ", *ps = " ps << * << ;ps = endl+
    ps 1 ps ; <<"add 1 to the ps pointer:\n"
    cout ; <<"ps = "
    cout << << ", *ps = " ps << * << "\n\n"ps ; <<"access two elements with array notation\n"
    cout ; <<"stacks[0] = "
    cout << [ 0 stacks]<<", stacks[1] = "
         << [ 1 stacks]<<; << endl"access two elements with pointer notation\n"
    cout ; <<"*stacks = "
    cout << * << ", *(stacks + 1) =  "stacks
         << * ( +1stacks ) <<; << endlsizeof
    cout ( )<<wages" = size of wages array\n" ; <<sizeof
    cout ( )<<pw" = size of pw pointer\n" ; return0
    ; }/*output
pw = 0x71601ff880, *pw = 10000
add 1 to the pw pointer:
pw = 0x71601ff888, *pw = 20000

ps = 0x71601ff87a, *ps = 3
add 1 to the ps pointer:
ps = 0x71601ff87c, *ps = 2

access two elements with array notation
stacks[0] = 3, stacks[1] = 2
access two elements with pointer notation
*stacks = 3, *(stacks + 1) =  2
24 = size of wages array
8 = size of pw pointer
*/
int
main

指针和字符串

直接看个例子:

( void)usingnamespace {
    ; char std[
    20 animal]="bear" ; //将char指针初始化为指向一个字符串,“wren”实际表示的是字符串的地址//因此这条语句将“wren”的地址赋值给了bird指针
    const
    char
    * = "wren"bird ; char*
    ; // 未初始化ps<<                  <<

    cout " and " animal ; <<<<
    cout "\n" bird ; // cout << ps << "\n";      //may display garbage, may cause a crash<<
    "Enter a kind of animal: "

    cout ; ;// ok if input < 20 chars
    cin >> animal//            point to allocated space              //这两个指针指向用一块内存和字符串
    // cin >> ps; Too horrible a blunder to try; ps doesn't
    =
    ;
    ps << animal<<                // 注意将animal赋给ps并不会复制字符串,只是复制地址。


cout "!\n" ps ; // ok, same as using animal<< "Before using strcpy():\n" cout ; <<<< //因为是char*类型。


所以需要强转为指针的地址类型 cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps //创建新的内存空间 endl= new ps char [ strlen()+animal1 ] ;// get new storage//第一个参数是目标地址,第二个参数是要复制字符串的地址 strcpy ( ,)ps; animal// 将字符串复制到新存储<< "After using strcpy():\n" cout ; <<<< cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps delete endl[ ];// cin.get(); ps// cin.get(); return 0 ; }/*output bear and wren Enter a kind of animal: dog dog! Before using strcpy(): dog at 0xad4c9ffd40 dog at 0xad4c9ffd40 After using strcpy(): dog at 0xad4c9ffd40 dog at 0x28c890018d0 */ for (

C-风格字符串比较

C++将C-风格字符串视为地址,所以使用关系运算符比较,无法得到满意的结果。



需要使用strcmp()函数来比较。



1.该函数接受两个字符串地址作为参数,这意味着参数可以是指针,字符串常量或者是字符数组名。



2.如果第一个字符串按字母顺序排在第二个字符串之前, 则strcmp()将返回一个负数值;
3.如果第一个字符串按字母顺序排在第 二个字符串之后, 则strcpm()将返回一个正数值。


使用关系运算符可以比较字符,因为字符实际上是整数。


例如:

= 'a'ch ; <='z' ch ; ++) ch<<;
	cout int chmain

例如,比较单词:

( void)usingnamespace {
    ; char std[
    5 word]="?ate" ; for(
    char ='a' ch ; strcmp( ,"mate"word) ;++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is 11"
    cout << << ; word return endl0
    ; }/*
?ate
aate
bate
cate
date
eate
fate
gate
hate
iate
jate
kate
late
After loop ends, word is 11mate
*/
int
main

7.比较string类字符串

( )usingnamespace {
    ; = std"?ate"
    string word ; for(

    char ='a' ch ; !="mate" word ; ++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is "
    cout << << ; word // cin.get(); endlreturn
    0
    ; }char
*

因为string类重载了!= 所以可以这样比较。


函数使用指针处理数组

1.C++将数组名解释其第一个元素的地址:
cookies == &cookies[0];
2.如果将取地址符&用于数组名时,将返回整个数组的地址。



3.在c++中,当且仅当用于函数头或函数原型中,int * arr和int arr [] 的含义才是相同的。


都意味着arr是一个int指针。



4.数组表示法 int arr []提醒用户,arr不仅指向int,还指向int数组的第一个int

请记住这个恒等式:

arr[i] == *(ar + i)
&arr[i] == ar + i
将指针(包括数组名) 加1’实际上是加上了一个与指针指向的类型的长度(以字节为单位)
相等的值。


对于遍历数组而言, 使用指针加法和数组下标时等效的。


buildstr (char,int c) ; n// prototypeint     main
( )usingnamespace {
    ; int std;
    char times;
    << ch"Enter a character: "

    cout ; ;<<
    cin >> ch"Enter an integer: "
    cout ; ;char
    cin >> times*
    = buildstrps ( ,)ch; times<<<<
    cout ; ps delete endl[
    ];// free memory ps=                   buildstr
    ps ( '+',20) ;// reuse pointer<<         <<
    cout "-DONE-" ps << << ; ps delete endl[
    ];// free memory ps// cin.get();                   // cin.get();
    return
    0
    ; }// builds string made of n c characters
char

*
buildstr (char,int c) char n* {
    = newpstr char [ +1n ] ;[]
    pstr=n';' // terminate string while(         --
    0 )n[ > ]=
        pstr;n// fill rest of string return c;        }
    /*output
Enter a character: z
Enter an integer: 10
zzzzzzzzzz
++++++++++++++++++++-DONE-++++++++++++++++++++
*/ pstr声明应像函数原型那样指出有关函数的信息
double
pam
函数指针基础使用

函数也有地址。


函数的地址是存储其机器语言代码的内存的开始地址。



可以编写将另一个函数的地址作为参数的函数。


这样第一个函数将能够找到第二个函数, 并运行它。


与直接调用另一个函数相比,这种方法很笨拙, 但它允许在不同的时间传递不同函数的地址, 这意味着可以在不同的时间使用不同的函数。


1.获取函数的地址
使用函数名即可。


(后面没有参数)例如:think()是一个函数,则think就是该函数的地址。



2.声明函数指针
我们在声明指向某种数据类型的指针时,必须指定指针指向的类型。



声明指向函数的指针时,也必须指定指针指向的函数类型。


这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。


也就是说, (


int );double(*
) (intpf);=;//注意, pam()的特征标和返回类型必须与pf 相同//通常,要声明指向特定类型的函数的指针, 可以首先编写这种函数的原型, 然后用(*pf)替换 函数名。


这样pf就是这类函数的指针。


pf double pam= pam ( x 4 );double=( * y ) (5pf);//-------------------------------double= pf ( y2 5 );#defineFLOAT_POINTER //上面使用pf和(*pf)等价。


//一种认为pf时函数指针,而*pf是函数,因此将(*pf)()用作函数调用。


//一种认为函数名是指向该函数的指针,指向函数的指针的行为应该和函数名相似。


怎么理解?
为了提供正确的运算符优先级,必须在声明中使用括号将pf括起。



括号的优先级比 * 运算符高,因此
pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf 是一个指向函数的指针。


typedef关键字

C++为类型建立别名的方式有两种。



一种是预处理器:
#define BYTE char// preprocessor replaces BYTE with char
这样, 预处理器将在编译程序时用 char 替换所有的 BYTE,从而使 BYTE 成为 char 的别名。


笫二种方法是使用 C++(和 C) 的关键字 typedef 来创建别名。



例如要将 byte 作为 char 的别名,可以这样做:
typedef char byte; / / makes byte an alias for char
下面是通用格式:
typedef typeName aliasName;
换句话说, 如果要将 aliasName 作为某种类型的别名, 可以声明 aliasName, 如同将 aliasName 声明为 这种类型的变量那样, 然后在声明的前面加上关键宇 typedef。


例如,要让 byte_pointer 成为 char 指针的别名, 可将 byte_pointer 声明为 char 指针, 然后在前面加上 typedef:
typedef char* byte_pointer;// pointer to char type
注意:某些场景下使用#define,不适用。


例如:

float* , ; //只有pa是float指针变量,pb只是float类型
FLOAT_POINTER patypedefpbdoubleconst

上面代码使用typedef方法不会有这样的问题。


typedef不会创建新类型,而只是为已有的类型建立一个新的名称。


使用typedef简化函数指针

double * ( * )(constp_fundouble*, int );=;//p1 points to the f1() function
p_fun p1 const 	f1int=
虚函数的工作原理

使用virtual的原因是:希望同一个方法在派生类和基类中的行为不同。


其中有2种机制来实现:
1.在派生类中重新定义基类的方法
2.使用虚方法

通常, 编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员。


隐藏成员中保存了一个指向函数
地址数组的指针。


这种数组称为虚函数表 (virtual function table, vtbl)。


虚函数表中存储了为类对象进行声
明的虚函数的地址。


例如,
1.基类对象包含一个指针, 该指针指向基类中所有虚函数的地址表。



2.派生类对象将包含一个指向独立地址表的指针。



3.如果派生类提供了虚函数的新定义, 该虚函数表将保存新函数的地址;
4.如果派生类没有重新定义虚函数, 该 vtbl 将保存函数原始版本的地址。



5.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。






上图解释:
对象A有两个虚函数vfunc1() 和vfunc2(),对象B复写了vfunc1(),并且有自己的func2() 对象C 也覆写了vfunc1()。



总结一下:
1 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生 出来的类) 中是虚的。



2 如果使用指向对象的引用或指针来调用虚方法, 程序将使用为对象类型定义的方法, 而不使用为引用或指针类型定义的方法。


这称为动态联编或晚期联编。


这种行为非常重要, 因为这样基类指 针或引用可以指向派生类对象。



3 如果定义的类将被用作基类, 则应将那些要在派生类中重新定义的类方法声明为虚的。



4 如果派生类没有重新定义函数, 将使用该函数的基类版本。


如果派生类位于派生链中, 则将使用最新的虚函数版本, 例外的情况是基类版本是隐藏的。


const使用


decltype

decltype(f())sum = x;//sum的类型就是函数f的返回类型
它的作用是选择并返回 *** 作数的数据类型。


在这个过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。



需要注意以下几点:
1.如果decltype使用的表达式是一个变量,则decltype返回该变量的类型。


(包括顶层const和引用在内。


0 , ci& =; decltypecj ( Cl)
=0ci;x //x的类型是const int decltype()
=;cj//y的类型是const int&,y绑定到变量xy decltypex()
;//错误z是一个引用,必须初始化cjintz=42

2.如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。



3.有些表达式将向decltype返回一个引用类型。


一般来说,意味着该表达式的结果对象能作为一条赋值语句的左值。


int i* =&; p  int &i=
; decltyper ( i+
//r是一个引用,因此decltype(r)的结果是引用类型。


r+0是一个具体的值 0)r;//加法的结果是int 因此b是一个为初始化的int.decltypeb(* //表达式的 *** 作时解引用,则decltype将得到引用类型。


);//错误的!!!p#cinclude#

解引用指针可以得到指针所指的对象。


而且还能给这个对象赋值。


4.decltype ((variable))(注意是双层括号)的结果永远是引用,而 decltype(variable)结果只有当variable本身就是一个引用时才是引用。


includestruct 
Adouble 
 
; } { ; xconst *;
decltype A( a)
 
;// y 的类型是 double(其声明类型)a->xdecltype y(       (
))=a->x;// z 的类型是 const double&(左值表达式) z template y< typename
 
T,typename Uauto add (>
, )decltypeT t( U u+ -> )// 返回类型依赖于模板形参t // C++14 开始可以推导返回类型 ureturn +
{                                     ;
    } tintumain
(
 
) int=33 
{
    ; i decltype ()
    =*i2 j ; i :: <<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j :: <<"i 和 j 的类型相同吗?"
 
    std<<cout ( ::
              < decltypestd(is_same_v),decltypei() ?"相同"j:> "不同" ) << '\n'; auto =[
 
    ] f ( int,int) aint return b* -> ;
    {
        } a ; bdecltype
    ()
 
    =;f// lambda 的类型是独有且无名的 g = ff (
    i 2 ,2); =g(
    j 3 ,3); ::<<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j } /*output
i = 33, j = 66
i 和 j 的类型相同吗?相同
i = 4, j = 9
*/void
f
(
模板别名using

用法:
对命名空间的 using 指令及对命名空间成员的 using 声明.

) ;namespacevoidg
 
( A
{
    ) ;}namespaceusing
::
 
; X
{
    // 全局 f 现在作为 ::X::f 可见 usingf::        ;
    // A::g 现在作为 ::X::g 可见 Ausingg::       ,
    :: A;g//(C++17)OK:命名空间作用域允许双重声明 A}gvoid h
(
 
) X::f
{
    ();// 调用 ::fX:: g
    ();// 调用 A::g}# include
// 基类 B

对类成员的 using 声明.

structB 
 
virtual
void f
{
    ( int )::<<"B::f\n" { std;cout } voidg (
    char )::<<"B::g(char)\n"        { std;cout } voidh (
    int )::<<"B::h\n"         { std;cout } protected: int
;// B::m 是受保护的
    typedef mint ;
    } ; value_type// 派生类 D
structD
 
:
B using :: ;
{
    // D::m 是公开的 Busingm:: ;
    // D::value_type 是公开的 B// using 的非必要使用场合:value_type// B::f 已经公开,它作为 D::f 公开可用, using
 
    ::
    ;
    // 并且下面的 D::f 和上面的 B::f 有相同的签名。


void Bff( int )::<<"D::f\n" { std;cout } // D::f(int) **覆盖** B::f(int)// using 的必要使用场合: // B::g(char) 被 D::g(int) 隐藏, // 如果不用 using B::g,将 char 传递给 D::g(char) 实际上调用的是下面定义的 D::g(int), using :: // 除非它在这里通过 using B::g 显式暴露为 D::g(char)。


; // 因为后者隐藏了 B::g(char),并且 char 形参会因此隐式转型到 int。


// 将 B::g(char) 作为 D::g(char) 暴露, Bvoidgg ( // 后者与下面定义的 D::g(int) 完全是不同的函数。


int )::<<"D::g(int)\n" { std;cout } // g(int) 与 g(char) 均作为 D 的成员可见// using 的非必要使用场合: // B::h 已经公开,它作为 D::h 公开可用, using :: ; // 并且下面的 D::h 和上面的 B::h 有相同的签名。


void Bhh( int )::<<"D::h\n" { std;cout } // D::h(int) **隐藏** B::h(int)} ; int main( ) ;&= { D d; B// b.m = 2; // 错误:B::m 受保护 b . d= 1 d;m // 受保护的 B::m 可以作为公开的 D::m 访问 .f ( b1);// 调用派生类的 f().f ( d1);// 调用派生类的 f()::<< "----------\n" std;cout . g( d1);// 调用派生类的 g(int).g ( d'a');// 调用基类的 g(char),它**只是因为**// 派生类中用到了 using B::g; 才会暴露:: << "----------\n" std;cout . h( b1);// 调用基类的 h().h ( d1);// 调用派生类的 h()}/*output D::f D::f ---------- D::g(int) B::g(char) ---------- B::h D::h */ # include #

类型别名与别名模板声明 (C++11 起).

include# 
include// 类型别名,等同于 
// typedef std::ios_base::fmtflags flags;using 
 
=
::
:: flags ; std// 名字 'flags' 现在指代类型:ios_base=fmtflags::
::
flags fl ; std// 类型别名,等同于ios_base// typedef void (*func)(int, int);decusing
 
=
void
( func * ) (int, int); // 名字 'func' 现在指代函数指针:voidexample
(
int ,int)} =; {// 别名模板
func f template example<
 
class
Tusing= *>
; ptr < Tint; 
// 名字 'ptr' 现在是指向 T 的指针的别名
ptr// 用于隐藏模板形参的别名模版template> x<
 
class
CharTusing= ::>
< mystring , std::basic_string<CharT; std<char_traitscharCharT>>;
mystring// 别名模板可引入成员 typedef 名template> str<
 
typename
TstructContainer using>
= ; { } value_type ; T// 可用于泛型编程 template<
typename
ContainerTypevoidg (>
const &)typename ContainerTypeContainerType c:: { ; }// 用于简化 std::enable_if 语法的类型别名value_type ntemplate <
 
typename
Tusing= typename>
T Invoke :: ; template<typetypename
Conditionusing= <>
:: EnableIf < Invoke::std;enable_iftemplateCondition<value>>typename
T,typename =< :: < EnableIfintstdfpoly_onlyis_polymorphic(T>>>
) return1T t; { } structS virtual
 
~ S { ( )}}; {int main(
 
) <int; 
{
    Containerg(> c)
    ;// Container::value_type 将在此函数中是 intc//  fpoly_only(c); // 错误:被 enable_if 禁止; fpoly_only
(
    S s)
    ;// OK:被 enable_if 允许s}struct A
A
explicit关键字

指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。



声明时不带函数说明符 explicit 的拥有单个无默认值形参的 (C++11 前)构造函数被称作转换构造函数。


( int
{
    )}// 转换构造函数A { (      int
    ,int)} // 转换构造函数(C++11)operator { bool (
    ) constreturntrue ; { } }; struct
Bexplicit
 
B (
{
    int )}explicitB { (
    int ,int)} explicitoperator { bool
    ( ) constreturntrue ; { } }; int
main(
 
) =1;
{
    A a1 // OK:复制初始化选择 A::A(int) a2(      2
    A );// OK:直接初始化选择 A::A(int)4,       5
    A a3 {}; // OK:直接列表初始化选择 A::A(int, int)=4   ,
    A a4 5 {}; // OK:复制列表初始化选择 A::A(int, int)=( )
    A a5 1 ;A// OK:显式转型进行 static_castif(   )
    ; // OK:A::operator bool()a1bool =      ;
    // OK:复制初始化选择 A::operator bool() na1 bool a1= static_cast
    < na2 bool ();>// OK:static_cast 进行直接初始化a1//  B b1 = 1;      // 错误:复制初始化不考虑 B::B(int)b2 (
 
2
    B );// OK:直接初始化选择 B::B(int)4,       5
    B b3 {}; // OK:直接列表初始化选择 B::B(int, int)//  B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)=   (
)
    B b5 1 ;B// OK:显式转型进行 static_castif(   )
    ; // OK:B::operator bool()b2//  bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool() bool      =
static_cast
    < nb2 bool ();>// OK:static_cast 进行直接初始化b2} 
[+++]
)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
C++易忘知识点整理_C_内存溢出

C++易忘知识点整理

C++易忘知识点整理,第1张

char类型

C++对字符使用单引号,对字符串使用双引号。


字符用单引号括起,表示字符的数值编码。


int main(void) {
    using namespace std;
    char ch = 'M';       // 将 M 的 ASCII 码分配给 ch
    int i = ch;          // 将相同的代码存储在 int 中
    cout << "The ASCII code for " << ch << " is " << i << endl;
    cout << "Add one to the character code:" << endl;
    ch = ch + 1;          // 更改 ch 中的字符代码
    i = ch;               // 在 i 中保存新的字符代码
    //输出时,cout将值78转换为所显示的字符N;
    cout << "The ASCII code for " << ch << " is " << i << endl;
    // using the cout.put() member function to display a char
    cout << "Displaying char ch using cout.put(ch): ";
    cout.put(ch);
    // using cout.put() to display a char constant
    cout.put('!');
    cout << endl << "Done" << endl;
    return 0;
}
/*输出结果
The ASCII code for M is 77
Add one to the character code:
The ASCII code for N is 78
Displaying char ch using cout.put(ch): N!
Done
*/
数组

声明数组的通用格式:typeName arrayName[arraySize]
注意:数组最后一个元素的索引比数组长度小1.如下图

初始化方式:

int cards[4] = {3,4,5,6,78};
int cards[4] {3,4,5,6,78}; //c++ 11
int cards[4];

上面几种方式都可以。


C-风格字符串

C-风格字符串具有一种特殊的性质: 以空字符 (null character) 结尾,空字符被写作\0, 其 ASCII 码为0, 用来标记字符串的结尾。


char dog[8]={ 'b', 'e', '矿, 'U I I '文, I', 'I', 'I'};// not a string
char cat[8]={'f'' 'a', 't'' 'e'' 's'' 's', 'a','}'; //a stringchar
字符串常量(字符串字面值)
[ bird]ll= "Mr. Cheeps" ;char 
[ fish]= "Bubbles" ;int 

在确定存储宇符串所需的最短数组时, 别忘了将结尾的空宇符计算在内。



数组中使用字符串
main (void)using {
    namespace ; stdconst
    int = Size 15 ;char
    [ name1]Size;// 空数组               char
    [ name2]Size= "C++owboy" ;// 初始化数组  <<
    cout "Howdy! I'm " << ; name2<<
    cout "! What's your name?\n" ;;
    cin >> name1<<
    cout "Well, " << << name1 ", your name has " ;//另外, strlen()只计算可见的字符, 而不把空字符计算在内.
    //strlen返回的是存储在数组中的字符串的长度, 而不是数组本身的长度。


<< cout strlen ()name1<< " letters and is stored\n" ;//计算大小 << cout "in an array of " << sizeof ()name1<< " bytes.\n" ;<< cout "Your initial is " << [ name10]<< ".\n" ;[ name23]= ';' // set to null character<< "Here are the first 3 characters of my name: " cout ; <<<< cout ; name2 return endl0 ; }/*打印 Howdy! I'm C++owboy! What's your name? kim Well, kim, your name has 3 letters and is stored in an array of 15 bytes. Your initial is k. Here are the first 3 characters of my name: C++ */ int *

上面程序使用‘\0’截断了数组,具体原理如下图:

使用new创建动态数组
= new psome int [ 10];//创建一个包含10个int元素的数组//new运算符返回第一个元素的地址
//注意:使用new创建的数组,需要delete[]psome
int
main

怎么访问数组的元素?
只要把指针当作数组名使用(c++将数组名解释为地址)即可。


对于第一个元素,可以使用psome[0],第二个元素使用psome[1],依此类推。



指针和数组基本等价的原因在于指针算术和C++内部处理数组的方式。



例如:

将整数变量加1后,其值将增加1;但将指针变量加1后, 增加的量等千它指向的类型
的字节数。


将指向double的指针加1后,如果系统对double使用 8个字节存储, 则数值将增加 8;将指向
short的指针加1后, 如果系统对sho rt 使用2 个字节存储, 则指针值将增加2。


( void)usingnamespace {
    ; double std[
    3 wages]=10000.0 , {20000.0, 30000.0} ;short[
    3 stacks]=3 , {2, 1} ;// 这里有两种获取数组地址的方法double
    *
    = ;pw // 数组名 = 地址 wagesshort     *
    = &ps [ 0stacks];// 或使用地址运算符//带数组元素 <<
    "pw = "
    cout << << ", *pw = " pw << * << ;pw = endl+
    pw 1 pw ; <<"add 1 to the pw pointer:\n"
    cout ; <<"pw = "
    cout << << ", *pw = " pw << * << "\n\n"pw ; <<"ps = "
    cout << << ", *ps = " ps << * << ;ps = endl+
    ps 1 ps ; <<"add 1 to the ps pointer:\n"
    cout ; <<"ps = "
    cout << << ", *ps = " ps << * << "\n\n"ps ; <<"access two elements with array notation\n"
    cout ; <<"stacks[0] = "
    cout << [ 0 stacks]<<", stacks[1] = "
         << [ 1 stacks]<<; << endl"access two elements with pointer notation\n"
    cout ; <<"*stacks = "
    cout << * << ", *(stacks + 1) =  "stacks
         << * ( +1stacks ) <<; << endlsizeof
    cout ( )<<wages" = size of wages array\n" ; <<sizeof
    cout ( )<<pw" = size of pw pointer\n" ; return0
    ; }/*output
pw = 0x71601ff880, *pw = 10000
add 1 to the pw pointer:
pw = 0x71601ff888, *pw = 20000

ps = 0x71601ff87a, *ps = 3
add 1 to the ps pointer:
ps = 0x71601ff87c, *ps = 2

access two elements with array notation
stacks[0] = 3, stacks[1] = 2
access two elements with pointer notation
*stacks = 3, *(stacks + 1) =  2
24 = size of wages array
8 = size of pw pointer
*/
int
main

指针和字符串

直接看个例子:

( void)usingnamespace {
    ; char std[
    20 animal]="bear" ; //将char指针初始化为指向一个字符串,“wren”实际表示的是字符串的地址//因此这条语句将“wren”的地址赋值给了bird指针
    const
    char
    * = "wren"bird ; char*
    ; // 未初始化ps<<                  <<

    cout " and " animal ; <<<<
    cout "\n" bird ; // cout << ps << "\n";      //may display garbage, may cause a crash<<
    "Enter a kind of animal: "

    cout ; ;// ok if input < 20 chars
    cin >> animal//            point to allocated space              //这两个指针指向用一块内存和字符串
    // cin >> ps; Too horrible a blunder to try; ps doesn't
    =
    ;
    ps << animal<<                // 注意将animal赋给ps并不会复制字符串,只是复制地址。


cout "!\n" ps ; // ok, same as using animal<< "Before using strcpy():\n" cout ; <<<< //因为是char*类型。


所以需要强转为指针的地址类型 cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps //创建新的内存空间 endl= new ps char [ strlen()+animal1 ] ;// get new storage//第一个参数是目标地址,第二个参数是要复制字符串的地址 strcpy ( ,)ps; animal// 将字符串复制到新存储<< "After using strcpy():\n" cout ; <<<< cout " at " animal << ( int *) <<; animal << endl<< cout " at " ps << ( int *) <<; ps delete endl[ ];// cin.get(); ps// cin.get(); return 0 ; }/*output bear and wren Enter a kind of animal: dog dog! Before using strcpy(): dog at 0xad4c9ffd40 dog at 0xad4c9ffd40 After using strcpy(): dog at 0xad4c9ffd40 dog at 0x28c890018d0 */ for (

C-风格字符串比较

C++将C-风格字符串视为地址,所以使用关系运算符比较,无法得到满意的结果。



需要使用strcmp()函数来比较。



1.该函数接受两个字符串地址作为参数,这意味着参数可以是指针,字符串常量或者是字符数组名。



2.如果第一个字符串按字母顺序排在第二个字符串之前, 则strcmp()将返回一个负数值;
3.如果第一个字符串按字母顺序排在第 二个字符串之后, 则strcpm()将返回一个正数值。


使用关系运算符可以比较字符,因为字符实际上是整数。


例如:

= 'a'ch ; <='z' ch ; ++) ch<<;
	cout int chmain

例如,比较单词:

( void)usingnamespace {
    ; char std[
    5 word]="?ate" ; for(
    char ='a' ch ; strcmp( ,"mate"word) ;++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is 11"
    cout << << ; word return endl0
    ; }/*
?ate
aate
bate
cate
date
eate
fate
gate
hate
iate
jate
kate
late
After loop ends, word is 11mate
*/
int
main

7.比较string类字符串

( )usingnamespace {
    ; = std"?ate"
    string word ; for(

    char ='a' ch ; !="mate" word ; ++) ch<<<< {
        cout ; word [ endl0
        word]=; } ch<<
    "After loop ends, word is "
    cout << << ; word // cin.get(); endlreturn
    0
    ; }char
*

因为string类重载了!= 所以可以这样比较。


函数使用指针处理数组

1.C++将数组名解释其第一个元素的地址:
cookies == &cookies[0];
2.如果将取地址符&用于数组名时,将返回整个数组的地址。



3.在c++中,当且仅当用于函数头或函数原型中,int * arr和int arr [] 的含义才是相同的。


都意味着arr是一个int指针。



4.数组表示法 int arr []提醒用户,arr不仅指向int,还指向int数组的第一个int

请记住这个恒等式:

arr[i] == *(ar + i)
&arr[i] == ar + i
将指针(包括数组名) 加1’实际上是加上了一个与指针指向的类型的长度(以字节为单位)
相等的值。


对于遍历数组而言, 使用指针加法和数组下标时等效的。


buildstr (char,int c) ; n// prototypeint     main
( )usingnamespace {
    ; int std;
    char times;
    << ch"Enter a character: "

    cout ; ;<<
    cin >> ch"Enter an integer: "
    cout ; ;char
    cin >> times*
    = buildstrps ( ,)ch; times<<<<
    cout ; ps delete endl[
    ];// free memory ps=                   buildstr
    ps ( '+',20) ;// reuse pointer<<         <<
    cout "-DONE-" ps << << ; ps delete endl[
    ];// free memory ps// cin.get();                   // cin.get();
    return
    0
    ; }// builds string made of n c characters
char

*
buildstr (char,int c) char n* {
    = newpstr char [ +1n ] ;[]
    pstr=n';' // terminate string while(         --
    0 )n[ > ]=
        pstr;n// fill rest of string return c;        }
    /*output
Enter a character: z
Enter an integer: 10
zzzzzzzzzz
++++++++++++++++++++-DONE-++++++++++++++++++++
*/ pstr声明应像函数原型那样指出有关函数的信息
double
pam
函数指针基础使用

函数也有地址。


函数的地址是存储其机器语言代码的内存的开始地址。



可以编写将另一个函数的地址作为参数的函数。


这样第一个函数将能够找到第二个函数, 并运行它。


与直接调用另一个函数相比,这种方法很笨拙, 但它允许在不同的时间传递不同函数的地址, 这意味着可以在不同的时间使用不同的函数。


1.获取函数的地址
使用函数名即可。


(后面没有参数)例如:think()是一个函数,则think就是该函数的地址。



2.声明函数指针
我们在声明指向某种数据类型的指针时,必须指定指针指向的类型。



声明指向函数的指针时,也必须指定指针指向的函数类型。


这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。


也就是说, (


int );double(*
) (intpf);=;//注意, pam()的特征标和返回类型必须与pf 相同//通常,要声明指向特定类型的函数的指针, 可以首先编写这种函数的原型, 然后用(*pf)替换 函数名。


这样pf就是这类函数的指针。


pf double pam= pam ( x 4 );double=( * y ) (5pf);//-------------------------------double= pf ( y2 5 );#defineFLOAT_POINTER //上面使用pf和(*pf)等价。


//一种认为pf时函数指针,而*pf是函数,因此将(*pf)()用作函数调用。


//一种认为函数名是指向该函数的指针,指向函数的指针的行为应该和函数名相似。


怎么理解?
为了提供正确的运算符优先级,必须在声明中使用括号将pf括起。



括号的优先级比 * 运算符高,因此
pf(int)意味着pf()是一个返回指针的函数,而(*pf)(int)意味着pf 是一个指向函数的指针。


typedef关键字

C++为类型建立别名的方式有两种。



一种是预处理器:
#define BYTE char// preprocessor replaces BYTE with char
这样, 预处理器将在编译程序时用 char 替换所有的 BYTE,从而使 BYTE 成为 char 的别名。


笫二种方法是使用 C++(和 C) 的关键字 typedef 来创建别名。



例如要将 byte 作为 char 的别名,可以这样做:
typedef char byte; / / makes byte an alias for char
下面是通用格式:
typedef typeName aliasName;
换句话说, 如果要将 aliasName 作为某种类型的别名, 可以声明 aliasName, 如同将 aliasName 声明为 这种类型的变量那样, 然后在声明的前面加上关键宇 typedef。


例如,要让 byte_pointer 成为 char 指针的别名, 可将 byte_pointer 声明为 char 指针, 然后在前面加上 typedef:
typedef char* byte_pointer;// pointer to char type
注意:某些场景下使用#define,不适用。


例如:

float* , ; //只有pa是float指针变量,pb只是float类型
FLOAT_POINTER patypedefpbdoubleconst

上面代码使用typedef方法不会有这样的问题。


typedef不会创建新类型,而只是为已有的类型建立一个新的名称。


使用typedef简化函数指针

double * ( * )(constp_fundouble*, int );=;//p1 points to the f1() function
p_fun p1 const 	f1int=
虚函数的工作原理

使用virtual的原因是:希望同一个方法在派生类和基类中的行为不同。


其中有2种机制来实现:
1.在派生类中重新定义基类的方法
2.使用虚方法

通常, 编译器处理虚函数的方法是: 给每个对象添加一个隐藏成员。


隐藏成员中保存了一个指向函数
地址数组的指针。


这种数组称为虚函数表 (virtual function table, vtbl)。


虚函数表中存储了为类对象进行声
明的虚函数的地址。


例如,
1.基类对象包含一个指针, 该指针指向基类中所有虚函数的地址表。



2.派生类对象将包含一个指向独立地址表的指针。



3.如果派生类提供了虚函数的新定义, 该虚函数表将保存新函数的地址;
4.如果派生类没有重新定义虚函数, 该 vtbl 将保存函数原始版本的地址。



5.如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。






上图解释:
对象A有两个虚函数vfunc1() 和vfunc2(),对象B复写了vfunc1(),并且有自己的func2() 对象C 也覆写了vfunc1()。



总结一下:
1 在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类(包括从派生类派生 出来的类) 中是虚的。



2 如果使用指向对象的引用或指针来调用虚方法, 程序将使用为对象类型定义的方法, 而不使用为引用或指针类型定义的方法。


这称为动态联编或晚期联编。


这种行为非常重要, 因为这样基类指 针或引用可以指向派生类对象。



3 如果定义的类将被用作基类, 则应将那些要在派生类中重新定义的类方法声明为虚的。



4 如果派生类没有重新定义函数, 将使用该函数的基类版本。


如果派生类位于派生链中, 则将使用最新的虚函数版本, 例外的情况是基类版本是隐藏的。


const使用


decltype

decltype(f())sum = x;//sum的类型就是函数f的返回类型
它的作用是选择并返回 *** 作数的数据类型。


在这个过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。



需要注意以下几点:
1.如果decltype使用的表达式是一个变量,则decltype返回该变量的类型。


(包括顶层const和引用在内。


0 , ci& =; decltypecj ( Cl)
=0ci;x //x的类型是const int decltype()
=;cj//y的类型是const int&,y绑定到变量xy decltypex()
;//错误z是一个引用,必须初始化cjintz=42

2.如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。



3.有些表达式将向decltype返回一个引用类型。


一般来说,意味着该表达式的结果对象能作为一条赋值语句的左值。


int i* =&; p  int &i=
; decltyper ( i+
//r是一个引用,因此decltype(r)的结果是引用类型。


r+0是一个具体的值 0)r;//加法的结果是int 因此b是一个为初始化的int.decltypeb(* //表达式的 *** 作时解引用,则decltype将得到引用类型。


);//错误的!!!p#cinclude#

解引用指针可以得到指针所指的对象。


而且还能给这个对象赋值。


4.decltype ((variable))(注意是双层括号)的结果永远是引用,而 decltype(variable)结果只有当variable本身就是一个引用时才是引用。


includestruct 
Adouble 
 
; } { ; xconst *;
decltype A( a)
 
;// y 的类型是 double(其声明类型)a->xdecltype y(       (
))=a->x;// z 的类型是 const double&(左值表达式) z template y< typename
 
T,typename Uauto add (>
, )decltypeT t( U u+ -> )// 返回类型依赖于模板形参t // C++14 开始可以推导返回类型 ureturn +
{                                     ;
    } tintumain
(
 
) int=33 
{
    ; i decltype ()
    =*i2 j ; i :: <<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j :: <<"i 和 j 的类型相同吗?"
 
    std<<cout ( ::
              < decltypestd(is_same_v),decltypei() ?"相同"j:> "不同" ) << '\n'; auto =[
 
    ] f ( int,int) aint return b* -> ;
    {
        } a ; bdecltype
    ()
 
    =;f// lambda 的类型是独有且无名的 g = ff (
    i 2 ,2); =g(
    j 3 ,3); ::<<"i = "
 
    std<<cout << ", " << i "j = " <<
              << '\n' ; j } /*output
i = 33, j = 66
i 和 j 的类型相同吗?相同
i = 4, j = 9
*/void
f
(
模板别名using

用法:
对命名空间的 using 指令及对命名空间成员的 using 声明.

) ;namespacevoidg
 
( A
{
    ) ;}namespaceusing
::
 
; X
{
    // 全局 f 现在作为 ::X::f 可见 usingf::        ;
    // A::g 现在作为 ::X::g 可见 Ausingg::       ,
    :: A;g//(C++17)OK:命名空间作用域允许双重声明 A}gvoid h
(
 
) X::f
{
    ();// 调用 ::fX:: g
    ();// 调用 A::g}# include
// 基类 B

对类成员的 using 声明.

structB 
 
virtual
void f
{
    ( int )::<<"B::f\n" { std;cout } voidg (
    char )::<<"B::g(char)\n"        { std;cout } voidh (
    int )::<<"B::h\n"         { std;cout } protected: int
;// B::m 是受保护的
    typedef mint ;
    } ; value_type// 派生类 D
structD
 
:
B using :: ;
{
    // D::m 是公开的 Busingm:: ;
    // D::value_type 是公开的 B// using 的非必要使用场合:value_type// B::f 已经公开,它作为 D::f 公开可用, using
 
    ::
    ;
    // 并且下面的 D::f 和上面的 B::f 有相同的签名。


void Bff( int )::<<"D::f\n" { std;cout } // D::f(int) **覆盖** B::f(int)// using 的必要使用场合: // B::g(char) 被 D::g(int) 隐藏, // 如果不用 using B::g,将 char 传递给 D::g(char) 实际上调用的是下面定义的 D::g(int), using :: // 除非它在这里通过 using B::g 显式暴露为 D::g(char)。


; // 因为后者隐藏了 B::g(char),并且 char 形参会因此隐式转型到 int。


// 将 B::g(char) 作为 D::g(char) 暴露, Bvoidgg ( // 后者与下面定义的 D::g(int) 完全是不同的函数。


int )::<<"D::g(int)\n" { std;cout } // g(int) 与 g(char) 均作为 D 的成员可见// using 的非必要使用场合: // B::h 已经公开,它作为 D::h 公开可用, using :: ; // 并且下面的 D::h 和上面的 B::h 有相同的签名。


void Bhh( int )::<<"D::h\n" { std;cout } // D::h(int) **隐藏** B::h(int)} ; int main( ) ;&= { D d; B// b.m = 2; // 错误:B::m 受保护 b . d= 1 d;m // 受保护的 B::m 可以作为公开的 D::m 访问 .f ( b1);// 调用派生类的 f().f ( d1);// 调用派生类的 f()::<< "----------\n" std;cout . g( d1);// 调用派生类的 g(int).g ( d'a');// 调用基类的 g(char),它**只是因为**// 派生类中用到了 using B::g; 才会暴露:: << "----------\n" std;cout . h( b1);// 调用基类的 h().h ( d1);// 调用派生类的 h()}/*output D::f D::f ---------- D::g(int) B::g(char) ---------- B::h D::h */ # include #

类型别名与别名模板声明 (C++11 起).

include# 
include// 类型别名,等同于 
// typedef std::ios_base::fmtflags flags;using 
 
=
::
:: flags ; std// 名字 'flags' 现在指代类型:ios_base=fmtflags::
::
flags fl ; std// 类型别名,等同于ios_base// typedef void (*func)(int, int);decusing
 
=
void
( func * ) (int, int); // 名字 'func' 现在指代函数指针:voidexample
(
int ,int)} =; {// 别名模板
func f template example<
 
class
Tusing= *>
; ptr < Tint; 
// 名字 'ptr' 现在是指向 T 的指针的别名
ptr// 用于隐藏模板形参的别名模版template> x<
 
class
CharTusing= ::>
< mystring , std::basic_string<CharT; std<char_traitscharCharT>>;
mystring// 别名模板可引入成员 typedef 名template> str<
 
typename
TstructContainer using>
= ; { } value_type ; T// 可用于泛型编程 template<
typename
ContainerTypevoidg (>
const &)typename ContainerTypeContainerType c:: { ; }// 用于简化 std::enable_if 语法的类型别名value_type ntemplate <
 
typename
Tusing= typename>
T Invoke :: ; template<typetypename
Conditionusing= <>
:: EnableIf < Invoke::std;enable_iftemplateCondition<value>>typename
T,typename =< :: < EnableIfintstdfpoly_onlyis_polymorphic(T>>>
) return1T t; { } structS virtual
 
~ S { ( )}}; {int main(
 
) <int; 
{
    Containerg(> c)
    ;// Container::value_type 将在此函数中是 intc//  fpoly_only(c); // 错误:被 enable_if 禁止; fpoly_only
(
    S s)
    ;// OK:被 enable_if 允许s}struct A
A
explicit关键字

指定构造函数或转换函数 (C++11 起)或推导指引 (C++17 起)为显式,即它不能用于隐式转换和复制初始化。



声明时不带函数说明符 explicit 的拥有单个无默认值形参的 (C++11 前)构造函数被称作转换构造函数。


( int
{
    )}// 转换构造函数A { (      int
    ,int)} // 转换构造函数(C++11)operator { bool (
    ) constreturntrue ; { } }; struct
Bexplicit
 
B (
{
    int )}explicitB { (
    int ,int)} explicitoperator { bool
    ( ) constreturntrue ; { } }; int
main(
 
) =1;
{
    A a1 // OK:复制初始化选择 A::A(int) a2(      2
    A );// OK:直接初始化选择 A::A(int)4,       5
    A a3 {}; // OK:直接列表初始化选择 A::A(int, int)=4   ,
    A a4 5 {}; // OK:复制列表初始化选择 A::A(int, int)=( )
    A a5 1 ;A// OK:显式转型进行 static_castif(   )
    ; // OK:A::operator bool()a1bool =      ;
    // OK:复制初始化选择 A::operator bool() na1 bool a1= static_cast
    < na2 bool ();>// OK:static_cast 进行直接初始化a1//  B b1 = 1;      // 错误:复制初始化不考虑 B::B(int)b2 (
 
2
    B );// OK:直接初始化选择 B::B(int)4,       5
    B b3 {}; // OK:直接列表初始化选择 B::B(int, int)//  B b4 = {4, 5}; // 错误:复制列表初始化不考虑 B::B(int,int)=   (
)
    B b5 1 ;B// OK:显式转型进行 static_castif(   )
    ; // OK:B::operator bool()b2//  bool nb1 = b2; // 错误:复制初始化不考虑 B::operator bool() bool      =
static_cast
    < nb2 bool ();>// OK:static_cast 进行直接初始化b2} 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/578569.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-11
下一篇 2022-04-11

发表评论

登录后才能评论

评论列表(0条)

保存