C++高级编程总结​​C++高级编程笔记​

1. 头文件添加注释(文件名, 作者, 函数简介, 日期).

2. 当头文件数目较多时, 应将头文件放在include目录下, 源文件放在source目录下.

3. const常量有数据类型, 宏常量没有数据类型.

4. 对于重载赋值运算符, 应该用"引用传递"方式

5. 函数入口处用assert检查

6. 内存分配方式有三种, 从静态存储区域分配, 从栈上分配, 从堆上分配, 静态存储区包括全局变量, static变量等.

7. C++/C语言,没有办法知道指针所指的内存容量.

8. 指针消亡了, 不表示所指的内存会被自动释放。内存被释放了, 不表示指针会消亡或者成了NULL指针.

9. 指针被free或delete之后, 别忘记设置为NULL.

10. malloc不调用构造函数, new自动调用构造函数, free和delete类似.

11. 为什么要用重载? (1) 便于记忆。(2) 不得不, 比如说类的多个构造函数

12. 不能编译头文件

全局变量定义时, (直接赋值), extern无作用

全局变量声明时, extern告诉编译器, 定义部分在其它模块中

全局变量默认extern

13. 重载: 同一个类中, 或都是全局函数.

覆盖: 分别位于派生类与基类中, 函数名与参数都相同,有virtual关键字,用于多态.

隐藏: 分别位于派生类与基类中, 只要同名, 且非覆盖, 均为隐藏.

14. 函数参数的缺省值,只能出现在函数的声明中.

15. 操作符重载。调用时, 普通函数参数出现在圆括号内, 对于运算符, 参数出现在其左右两侧。定义时,可定义为全局函数和类的成员函数,后者比前者少了一个参数。

16. 类的构造次序,先构造基类,再构造构造函数的参数,再构造成员,再构造自己。析构完全相反。

17. String a("hello"); String b=a; 其实是调用了b的拷贝构造函数,最好写成String b(a).

18. 对于一个类,编译器默认生成4个函数,无参构造函数,拷贝构造函数,析构函数,赋值函数

19. 类的析构函数,应为虚函数, 多态

20. 对于非内部数据类型的输入参数,应该将“值传递”的方式,改为“const 引用传递”,目的是提高效率。例如,将void Func(A a),改为 void Func(const A &a)。

21. 引用被创建的同时,必须被初始化,一旦引用被初始化,就不能改变引用的关系。

22. 对比于C语言的函数,C++增加了重载,内联,const和virtual四种新机制,重载和内联机制,既可以用于全局函数,也可用于类的成员函数,const与virtual机制,仅用于类的成员函数

23. 赋值符号的重载,不能为友元,只能是类的成员函数

C++高级编程

C++编程学习

概述

C++高级编程总结_成员函数

 

 面向对象的方法简介

对象----存在即合理

抽象性–物以类聚

封装----事物的封闭性

继承----事物的相似性

多态----事物的多样性

面向对象方法(类与对象)

 C++高级编程总结_内联函数_02

 

 C++概述

1.什么是C++?

C to C++:

* C语言的超集

* 面向对象编程

* 可移植性,不牺牲性能和底层功能

C++ compiler:将C++代码翻译为C代码

 C++高级编程总结_c++_03

 

 2.C++适合?

* 算法

* 应用开发

* C/C++服务器

C++设计原则

 

C++设计成使用静态型别机制、与C同样高效,可移植的,多用途程序设计语言。

C++设计成直接的和广泛的,支持多种程序设计风格(程序化程序设计、数据抽象化、面向对象程 序设计、泛型程序设计)。

C++设计成给程序设计者,更多的选择,即使可能导致程序设计者选择错误。

C++设计成尽可能与C兼容,提供一个从C到C++的平滑过渡。

C++避免平台限定,或没有普遍用途的特性。

C++不使用会带来额外开销的特性。

C++设计成无需复杂的程序设计环境。

UNIX铁律: K.I.S.S-Keep It Simple,Stupid!

 C++高级编程总结_#include_04

 

 &参考

C++大学教程(推荐)

C++Primer

C++编程思想

 

&开发环境

Visual Studio 201X Community

Code::Block

Qt Creator

 

C++的第一个程序

第一个C++程序:

 

#include<iostream>

 

int main(void)

{

   std::cout<<"hello world!"<<std::endl;

 

    return 0;

}

 

流的概念及用途

 

C++的I/O是以字节流的形式实现的,流(stream)实际上就是一个字节序列。

输入流: 在输入操作中,字节从输入设备(如键盘、磁盘、网络连接等)流向内存;

输出流: 在输出操作中,字节从内存流向输出设备(如显示器、打印机、磁盘、网络连接等);

这里“流”,试图说明字符,随着时间顺序生成或消耗的。

输人/输出系统的任务,实际上就是以一种稳定、可靠的方式,在设备与内存之间传输数据。

C++并没有直接定义,进行输入输出的任何语句,这些功能是由标准IO库完成。

命名空间(namespace)

实际上就是,一个由程序设计者命名的内存区域,程序设计者,可以根据需要指定一些有名字的空间域,把一些全局实体,分别放在各个命名空间中,从而与其它全局实体分隔开来。

命名空间是ANSIC++引入的,可以由用户命名的作用域,用来处理程序中,常见的同名冲突。

std::cout std::cin std::endl

 

+使用命名空间:

using std::cout;

using namespace std;

 

#include<iostream>

using namespace std; //使用命名空间

 

int main(void)

{

    cout<<"hello world!"<<endl;

 

    return 0;

}

C++程序的执行过程

1.C++主要有三个编译阶段:预处理、转译成目标代码和链接(最后的两个阶段,一般才视为真正的“编译”)。

2.C++预处理命令和C基本相同

C++高级编程总结_内联函数_05

 

 C/C++的字符串比较

0.C语言字符串练习 char st[100];

0.C++语言字符串练习 string str;

1.检测字符串长度 int len = strlen(st);

1.检测字符串长度 int len = str.length();

2.字符串比较 strcmp(st1, st2);

2.字符串比较 str1.compare(str2);

3.在已有字符串后,追加新串 strcat(st1, st2); strncat(st1,st2,n);

3.在已有字符串后,追加新串 str1 += str2; str1.append(str2);

4.字符串拷贝 strcpy(st1,st2); strncpy(st1,st2, n);

4.字符串拷贝 str2 = str1; str2 = str1.substr();

5.字符串查找 where = strchr(st, ch)

5.字符串查找 where = str1.find(str2);

 

#include<iostream>

#include<string>

 

using namespace std;

 

int main(void)

{

    stringstr("hello");

    cout <<str.length() << endl;

    str.append("world!");//在已有字符串后追加新串

    cout << str<< endl;

 

    return 0;

}

 

C/C++数据类型与变量

 C++高级编程总结_内联函数_06

 

 C/C++变量

程序运行过程中,值能否发生改变,分为**常量和变量**

从变量作用域的大小考虑:全局变量,局部变量

全局变量:定义在所有的函数体之外,在程序开始运行时,分配存储空间,在程序结束时,释放存储空间

函数中定义的变量,称为局部变量(Local Variable)

从变量的生命周期考虑: 静态生存周期和动态生存周期

动态存储变量:变量仅在需要的时候,分配和占用内存

静态存储变量:变量在程序运行过程中,占用固定的内存

从变量的在内存中位置考虑:普通变量与指针变量

 C++高级编程总结_#include_07

 

 动态内存分配

所谓动态内存分配,指在程序运行期间,根据实际需要随时申请内存,在不需要时释放

 

new/delete是C++的运算符

用于申请动态内存和释放内存

new运算符的语法格式为:指针=new 数据类型;

• int *p=new int;

• int *p=new int[30];

delete运算符的功能,用来删除是用new创建的对象,或一般类型的指针,其格式如下:delete<指针名>

使用delete,也可以用来删除使用new创建的对象数组,其使用格式如下:delete [] 指针名

 

#include<iostream>

#include<string.h>

using namespace std;

 

int main()

{

    char *str;

    str = new char[10];

   strcpy(str,"hello");

    cout << str<< endl;

    delete []str;

   

    return 0;

}

 

杜绝“野指针”

 

指针用来存放某个变量的地址值的一种变量

 

“野指针”不是NULL指针,指向“垃圾”内存的指针,“野指针”的危险之处,在于if语句不起作用。

 

任何指针变量刚被创建时,不会自动成为NULL指针,缺省值是随机的。所以,指针变量在创建的同时,应当被初始化,要不将指针设置为NULL,要么指向合法的内存。

char *p = NULL;

char * str = (char *)malloc(100);

 

指针p被free或者delete之后,应设置为NULL。

 

指针操作超越了变量的作用范围

 

程序不能返回指向栈内存的指针或引用。

 

数组与指针

-任何一个数组的名字是一个常量指针,值是该数组的首元素的地址值

-在C++中引用一个数组元素,有两种等价的方法:

-下标法:如a[j]或p[j]形式,比较直观

-指针法:如*(a+j)或 *(p+j)形式,比较高效

-数组名作为函数形参时,在函数体内,失去了本身的内涵,仅仅是一个指针。

-指向数组的指针,另外一种变量类型(在linux或win32平台下,长度为4),仅仅意味着数组的存放地址;

 

数组指针与指针数组

 

总结

 

new运算符根据对象的类型,自动决定其大小,不使用sizeof运算符,malloc要指定分配存储空间的大小;

new返回指向此类型的指针,不用进行强制指针类型转换。malloc返回指向void*类型的指针。

如果在申请动态内存时,找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败

用free或delete释放内存之后,没有将指针设置为NULL。导致产生“野指针”。防止使用指针值为NULL的内存。

动态内存的申请与释放必须配对,防止内存泄露。

引用与函数传参

什么是引用?

 

引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

引用(&)在此是起标示作用,不是求地址运算,定义格式

<类型>&<引用名>(<变量名>);

<类型>&<引用名>=(<变量名>);

int a = 3;

int &m = a; //定义引用并初始化

int n = m; //将引用赋给变量

int *p = &m;

m = m + 5; // a = 8, 对引用的操作,就是对被引用者的操作

引用详解

初始化与赋值

 

定义引用时,必须初始化;

可以将一个引用赋予给某个变量;

引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名,作为其它变量名的别名。

指针和引用的区别

指针通过地址,间接访问某个变量

引用通过别名,直接访问某个变量

引用一般用作函数的参数,或函数的返回值

如果既要利用引用提高使用效率,又要保护传递给函数的数据,不在函数中被改变,就应当使用常引用。

引用的用途

 

C语言中没有引用,C++中才有引用,引用的主要用途,就是在函数传参和返回值上。

使用引用作为传递函数的参数,在内存中,没有产生实参的副本,直接对实参操作。

如果输入参数,以值传递的方式传递对象,宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,提高效率。

如果既要利用引用,调高使用效率,又要保护传递给函数的数据,不在函数中被改变,就应当使用常引用。

如果函数的返回值是一个对象,有些场合,用“引用传递”替换“值传递”,可以提高效率,有些场合不可以。

常引用

 

声明一个引用,不是新定义一个变量,只是该引用名是目标变量的一个别名,本身不是一种数据类型,引用不占存储单元。对引用取地址,就是对目标变量取地址

非const引用,只能绑定到该引用同类型的变量。const引用,可以绑定到不同,但相关类型的对象, 或着绑定到右值。

常引用的声明方式:

const 类型标示符 &引用名 = 目标变量名;

用这种方式声明的引用,不能通过引用,对变量的值进行修改,但目标自身任然能够改变,这是语言的不一致性。

#include<iostream>

 

using namespace std;

 

int main()

{

    double dvalue = 3.14;

    const int &pd =dvalue;

    dvalue = 50.6;

    cout << pd<<"\n"<<dvalue <<endl;

   

    return 0;

}

 

 

函数返回一个类型的引用

注意:不允许返回的引用,对应于一个局部变量

 

#include<iostream>

 

using namespace std;

 

int &getlnt(const int v)

{

    int *p = new int(v);

    return *p;

}

 

int main()

{

    int &n =getlnt(123456789);

    cout << n<< endl;

    int *pp = &n;

    delete pp;

 

    return 0;

}

 

return语句,不可返回指向“栈内存”的,“指针”,或者“引用”,该内存在函数体结束时,被自动销毁。

 

C/C++函数说明

C++函数说明

 

C/C++中的函数说明:

四个组成部分:返回类型、函数名、形式参数表、函数体

函数的申明与实现,函数的调用与传参(形参与实参)

函数与指针:指针函数与函数指针

C++增强了函数类型:

基础函数:内联函数,函数重载,模板函数(泛型编程)

成员函数:构造/析构函数,常成员函数,静态成员函数,虚函数

内联函数(inline)

内联(内嵌)函数,主要解决的是程序的运行效率问题。

解决手段,在编译阶段,编译器就会把每次调用内联函数的地方,都替换为该函数体内的代码。

引入原因:解决程序中,一些函数体代码不是很大,却有被频繁调用的函数,调用效率问题。

解决方法:以目标代码的增加为代价,换取时间上的节约。

 C++高级编程总结_c++_08

 

 当在一个函数定义或声明前,加上关键字inline,则把该函数定义为内联函数。

#include<iostream>

 

using namespace std;

 

inline int f(int x)

{

    return x * x;

}

 

int main()

{

    int x(2);

    cout << f(x)<< endl;

    cout << f(x +1) <<endl;

 

    return 0;

}

 

使用内联函数的注意事项

 

内联函数的定义,必须出现在该函数第一次被调用前;

内联函数不能有复杂的控制语句,如switch,goto和while。

递归函数不能是内联函数。类结构中所有的在类,说明内部定义的函数,都是内联函数。

内联函数具有与带参的宏定义#define,相同的作用和相似的机理,但内联函数具有宏定义所没有的优点,没有缺点,消除了宏定义的不安全性。

函数重载

函数重载又称为函数的多态性

指同一个函数名对应着多个不同的函数。

“不同”是指,这些函数的形参表,必须互不相同,或者是形参的个数不同,或者是形参是类型不同,或者是两者都不相同,否则,将无法实现函数重载。

下面是合法的重载函数:

int funcl (int ,int);

int funcl(int );

double funcl(int,long);

double funcl(long);

#include <iostream>/*函数重载*/

using namespace std;

int add(int,int);

double add(double,double);

int main()

{

cout<<add(10,20)<<endl;

cout<<add(10.0,20.5)<<endl;

}

int add(int x,int y)

{

cout<<"int"<<endl;

return x+y;

}

double add(double x,double y)

{

cout<<"double"<<endl;

return x+y;

}

 

注意:

 

重载函数的类型,即函数的返回类型,可以相同,也可以不同。如果仅仅是返回类型不同,函数名相同、形参表也相同,则是不合法的,编译器会报“语法错误”。如:

int funcl (int a,int b);

double funcl (int a,int b);

 

除形参名外都相同的情况,编译器不认为是重载函数,只认为是对同一个函数原型的多次声明。

 

在调用一个重载函数funcl()时,编译器必须判断函数名funcl,到底是指哪个函数。通过编译器,根据实参的个数和类型,对所有funcl()函数的形参一一进行比较,调用一个最匹配的函数。

 

带默认参数值的函数

 

在C++语言中调用函数时,通常要为函数的每个形参,给定对应的实参。若没有给出实参,按指定的默认值进行工作。

当一个函数既有定义,又有声明时,形参的默认值,必须在声明中指定,不能放在定义中指定。只有当函数没有声明时,才可以在函数定义中指定形参的默认值。

默认值的定义,必须遵守从右到左的顺序,如果某个形参没有默认值,则左边的参数就不能有默认值。如:

void funcl(int a, double b=4.5, int c=3);//合法

void funcl(int a=1, double b, int c=3);//不合法

在进行函数调用时,实参与形参按从左到右的顺序进行匹配,当实参的数目少于形参时,如果对应位置形参,没有设定默认值,就会产生编译错误;如果设定了默认值,编译器将没有对应实参的形参取默认值。

 

注意

 

形参的默认值,可以是全局常量、全局变量、表达式、函数调用,但不能为局部变量。例如,

下例不合法:

void funcl()

{

int k;

void g(int x=k);//k为局部变量

}

使用缺省参数时,主要满足函数重载条件;

void fun (int x, int y=0);

void fun(int x);

此时函数fun不能进行重载,因为编译器不能唯一确定,调用哪个函数(fun(3)或fun(3,0)均可)。

##封装性

 

封装是面向对象的特征之一,对象和类概念的主要特性。

 

封装,也就是把客观事物封装成抽象的类,类可以把自己的数据和方法,只让可信的类,或者对象操作,对不可信的进行信息隐藏。

 

具备封装性(Encapsulation)的面向对象程序设计,隐藏了某一方法的具体执行步骤,取而代之的是通过消息传递机制传送消息。

 

封装

 

关键字:public、protected、prviate

• 修饰成员变量和成员函数;

• 继承时使用

 

破坏封装:关键字friend

• 友元类和友元函数

 

封装好的结构体

 

#include <iostream>

#include <string.h>

using namespace std;

struct Person

{

    char *m_name;

    int m_age;

    void setName(constchar*);

    void setAge(int);

    void disp();

};

void Person::setName(const char* name)

{

    m_name = newchar[100];

    strcpy(m_name, name);

}

void Person::setAge(int age)/*作用域=作用域名+作用域符*/

{

    m_age = age;

}

void Person::disp()

{

    cout <<"Name:" << m_name << endl;

    cout <<"Age:" << m_age << endl;

}

int main()

{

    Person somebody;

   somebody.setName("DaXian666");

    somebody.setAge(23);

    somebody.disp();

}

 

定义类来实现数据隐藏

 

#include <iostream>

#include <string.h>

using namespace std;

class Person

{

private://数据

    char *m_name;

    int m_age;

public://函数

    void setName(constchar*);

    void setAge(int);

    void disp();

};

void Person::setName(const char*name)

{

    m_name = newchar[100];

    strcpy(m_name, name);

}

 

void Person::setAge(int age)

{

    m_age = age;

}

void Person::disp()

{

    cout << "Name:" <<m_name <<endl;

    cout <<"Age:" << m_age << endl;

}

 

int main()

{

    Person p;

   p.setName("DaXian666");

    p.setAge(23);

    p.disp();

}

 

如何实现数据隐藏

 

引入class类型

 

对数据成员进行保护

 

增加存取范围

• 私有成员private

• 保护成员protected

• 公共成员public

 

在C++中,class与struct的区别:

 

struct的缺省作用域为public

 

class的缺省作用域为private

 

类定义格式的构成

 

说明部分:说明类中的成员,包含数据成员的说明和成员函数的说明;

 

实现部分:对成员函数的定义

 

类的一般定义格式

 C++高级编程总结_#include_09

 

 

类与对象

 

C++是为了解决编写大程序过程中的困难而产生的。

对象:客观世界中任何一个事物,都可以看成一个对象( object )。

对象组成:

数据——描述对象的属性

函数——行为(操作代码),根据外界给的信息进行相应操作的代码,外界给的信息进行相应操作的代码。

具有相同的属性和行为的对象抽象为类(class )

 

类是对象的抽象

 

对象则是类的特例

 

类与对象

类中的权限

 

一般的做法:将需要被外界调用的成员函数指定为public,类的对外接口。

并非要求把所有成员函数都指定为public

有的函数不是准备为外界调用的,支持其它函数的操作,就应该指定为private 。

这种函数称为其它成员的工具函数(utility function),类外用户不能调用。

私有的成员函数只能被本类中的其它成员函数所调用,不能被类外调用。

成员函数可以访问本类中任何成员(包括私有的和公用的),可以引用在本作用域中有效的数据。

C++ 标准库

 

#pragma once

//上下等价

#ifndef BitFlip_H

#define BitFlip_H

//the content of BitFlip_H header file

#endif // !BitFlip

1. 切勿在头文件中,使用using指令,或者using 声明,否则,添加头文件,都要用这个空间。

 

2. 命名空间嵌套在17标准中,得到了极大的简化

 

3. 声明变量的时候不指定值,变量的值取决于这块内存的值

 

4. 允许if中加入初始化器

 

if (int a = 0;a>0) {

 

    }

5.

 

std::array<int, 3> arr = { 1,1,1 };//固定大小#include<array> 11标准

    intsize=std::size(arr);

6. C++程序中内存分为堆和栈,函数的执行一般在栈上进行,用完释放。明确的分配内存,一般是放在了堆上。(在函数中声明的,也要释放掉)

 

int* a;

    *a = 0;

    delete a;//delete[]a---删除数组

    a = nullptr; 删除指针后,最好进行置空操作,防止再次的使用

7.

 

//make_unique在超过作用域,或者被删除时,就会自动释放内存资源

    autoan=make_unique<BitFlip>();

    //共享指针,每次指定make_shared时,都会递增一个引用计数,表示数据又多了一个拥有者,当超过作用域时,就递减引用计数,直到为0时,释放资源

    auto an1 =make_shared<BitFlip>();

8.const多种用法(如果需要给函数传递对象,最好按const引用传递,这样可以防止多余的复制。如果需要改变对象,则需要传递非const引用)

 

//保护参数

void get(const string* a) {

    *a ="ddd";//不通过

}

 

 

const int a = 1;  //保证代码不会改变该变量的值

 

//变量x和引用变量re,指向同一个值,改变一个,都会变

    int x = 1;

    int& re = x;

9.auto 去除了const限定和引用 因为auto创建一个副本。除非用auto &

 

//第一,const函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针

//第二,任何不会修改数据成员的函数,都应该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其它非const成员函数,编译器将指出错误,无疑会提高程序的健壮性

const int& Get() const {

}