C++标准化组织
https://isocpp.org/std/status
http://open-std.org/JTC1/SC22/WG21/
why C++王者归来?
https://coolshell.cn/articles/6548.html
方法论
学任何知识点,都可以从三个方面来考虑?
> 是什么? what
> 怎么实现的? how
> 为什么? why
> 学习的深度
> 讲出来
> 听得懂
内存泄漏
野指针
内存踩踏
malloc用法
malloc底层怎么实现的? ==> 系统调用
为什么?
常量和宏的区别
#include <iostream>
using std::cout;
using std::endl;
//编辑 --> 预处理(预编译) --> 编译 --> 汇编 --> 链接 --> 可执行程序
// -E -S as -o
//
//概念 ==》沟通
//
//怎么表示学会了一个知识点?
// 完成任务 --> 讲出来 --> 别人还能够听的懂
//
//
//宏定义与const的区别?(概念题是最容易丢分)
//1. 发生时机不一样: 宏定义发生在预处理时,const关键字发生编译时
//2. 宏定义仅仅只做了字符串的替换,没有类型检查; const关键字有类型
检查,可以提前发现错误
//3. const关键字更推荐使用; 因为使用const关键字可以减小犯错误的概率
#define NUMBER 1024
void test0()
{
cout << "NUMBER = " << NUMBER << endl;
}
void test1()
{
const int number = 1;
cout << ">> number = " << number << endl;
//const int number2;//error, 必须要进行初始化
}
//常量指针 指针常量
//
// int(*)[5] int *[5]
//数组指针 指针数组
//
//int(*p)() int* p()
//函数指针 指针函数
void test2()
{
int number = 10;
int number2 = 100;
int * p1 = &number;
cout << "*p1 = " << *p1 << endl;
cout << "&p1 = " << &p1 << endl;
const int * p2 = &number;//常量指针(pointer to const)
cout << "*p2 = " << *p2 << endl;
cout << "&p2 = " << &p2 << endl;
//*p2 = 100;//error 不能通过该指针修改所指变量的值
p2 = &number2;//可以改变其指向
cout << "*p2 = " << *p2 << endl;
int const * p3 = &number;
//*p3 = 1000;//error
p3 = &number2;//可以改变其指向
int * const p4 = &number;//指针常量(const pointer)
*p4 = 1000;//可以通过该指针改变所指变量的值
//p4 = &number2;//error 不能改变指向
//两者都不能修改
const int * const p5 = &number;
}
int main(void)
{
//test0();
//test1();
test2();
return 0;
}
Hello.cc
#include <stdio.h> //c标准头文件带有.h后缀
#include <iostream> //C++标准头文件没有后缀.h
//是用模板实现的, 必须要知道其实现
using namespace std;//命名空间
int main(int argc, char ** argv)
{
printf("hello,world!
");//标准库函数
cout << "hello,world!" << endl;//cout 对象, 输出流运算符
// operator<<(cout, "hello,world!");
return 0;
}
命名空间namespace
#include <iostream>
using namespace std;//using编译指令, 它会一次性把std空间中
//的所有实体全部引进来
//
//要求:熟悉空间中的实体
//目前来说,不推荐使用
void wd_display();
void tls_display();
void cout()
{
}
namespace wd
{
void display()
{
cout << "wd::display() " << endl;
}
}//end of namespace wd
namespace tls
{
void display()
{
cout << "tls::display() " << endl;
}
}//end of namespace tls
int main(void)
{
wd::display();//:: 作用域限定符, 这是完整形式
tls::display();
return 0;
}
namespace2
#include <iostream>
using std::cout;// using声明机制, 简化操作, 不会把所有的实体引进来
using std::endl;
void wd_display();
void tls_display();
struct Example
{
int x;
int y;
};
//命名空间在一个文件之中可以出现多次
//相当于一个黑洞
namespace wd
{
int number = 10;
void show();//声明
}//end of namespace wd
namespace tls
{
void display()
{
cout << "tls::display() " << endl;
wd::show();
}
}//end of namespace tls
namespace wd
{
void display()
{
cout << "wd::display() " << endl;
tls::display();
}
void show()//实现
{
cout << "wd::show()" << endl;
//wd::display();//加上就会发生递归调用:
}
}//end of namespace wd
int main(void)
{
wd::display();//:: 作用域限定符
tls::display();
cout << "wd::number = " << wd::number << endl;
return 0;
}
namespace3
//代码风格的限定 ==> code review 代码走查
//#include "myhead.h" //自定义头文件放在C头文件的上面
#include <stdio.h> //头文件的顺序: C的头文件放在C++头文件的前面
#include <stdlib.h>
#include <iostream>
using std::cout;//选择区域
using std::endl;
int number = 10;
namespace wd
{
int number = 100;
namespace lwh
{
void display()
{
cout << "wd::lwh::display()" << endl;
}
}//end of namespace lwh
}//end of namespace wd
namespace tls
{
int number = 1000;
void display(int number)//形参number会屏蔽其他的number
{
cout << "number = " << number << endl;
cout << "wd::number = " << wd::number << endl;
cout << "tls::number = " << tls::number << endl;
cout << "全局变量 number = " << ::number << endl;//匿名命名空间
}
}//end of namespace tls
int test0(void)
{
using wd::lwh::display;
display();
return 0;
}
int main(void)
{
//display();
test0();
return 0;
}
new / delete malloc / free
#include <stdlib.h>
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
//malloc/free与new/delete表达式的区别?
//相同点: 都是用来申请堆空间
//
//不同点:
// 1. malloc/free是库函数; new/delete是表达式
// 2. malloc开空间时,并不会进行初始化;new表达式是可以进行初始化
void test0()
{
int * p0;
int * p1 = (int *)malloc(sizeof(int));//系统调用
::memset(p1, 0, sizeof(int));
//bzero();
*p1 = 10;
printf("*p = %d
", *p1);
free(p1);
int * p2 = (int *)malloc(sizeof(int) * 10);
free(p2);
}
void test1()
{
int * p1 = new int(1);
cout << "*p1 = " << *p1 << endl;
delete p1;
int * p2 = new int[10]();//对于数组的申请需要加上[]
//加上小括号是有初始化
//不加小括号是不会进行初始化
delete [] p2;
}
int main(void)
{
test0();
test1();
return 0;
}
函数重载
#include <stdio.h>
#include <iostream>
using std::cout;
using std::endl;
//C++语言支持函数重载
//实现原理: 名字改编(name mangling)
//具体步骤: 当函数名称相同时,会根据
//函数参数的类型、个数、顺序进行改编
int add(int x, int y)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
double add(double x, double y)
{
return x + y;
}
double add(int x, double y)
{
return x + y;
}
double add(double x, int y)
{
return x + y;
}
int main(void)
{
int a = 3, b = 4, c = 5;
double d1 = 1.1, d2 = 2.2;
cout << "add(a, b) = " << add(a, b) << endl;
cout << "add(a, b, c) = " << add(a, b, c) << endl;
cout << "add(d1, d2) = " << add(d1, d2) << endl;
printf("a = %d, b = %d ", a, b);
return 0;
}
指针和引用
#include <iostream>
using std::cout;
using std::endl;
//引用于指针的区别?
//相同点: 引用底层的实现还是指针, 引用于指针都有"地址"的概念
//
//不同点:
// 1. 引用是一个变量的别名,必须要进行初始化
// 指针是一个独立的实体,可以不进行初始化
// 2. 引用不是一个独立的实体
// 3. 引用一经绑定之后,就不会就再改变其指向;
// 指针是很灵活的,可以改变指向
void test0()
{
int number = 1;
//引用是变量的别名
int & ref = number;
cout << "ref = " << ref << endl;
cout << "number = " << number << endl;
ref = 10;
cout << ">> 修改引用之后:" << endl;
cout << "ref = " << ref << endl;
cout << "number = " << number << endl;
cout << "&ref = " << &ref << endl
<< "&number = " << &number << endl;
//int & ref2;//error 引用不能单独存在,必须要绑定到一个实体
//引用必须要进行初始化
}
//值传递 ==> 复制
#if 0
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
#endif
//地址传递 ==> 值传递 ==> 复制
void swap(int * px, int * py)
{
int temp = *px;
*px = *py;
*py = temp;
}
//引用作为函数的参数存在
// 引用传递 ==> 没有复制的开销,
//可以提高程序的执行效率,
//传递参数的方式更加直观,自然
void swap(int & x, int & y)
{
int temp = x;
x = y;
y = temp;
}
void test1()
{
int a = 3, b = 4;
cout << "a = " << a << ", b = " << b << endl;
swap(a, b);
//swap(&a, &b);
cout << "a = " << a << ", b = " << b << endl;
}
//引用还可以作为返回值存在
int array[5] = {1, 2, 3, 4, 5};
int func1()
{
int number = 5;
return number;//会进行复制
}
int & getNumber(int idx)
{
return array[idx];//如果返回值是引用,不会进行复制
}
//不能返回一个局部变量的引用
int & func2()
{ //当函数执行完毕时,局部变量生命周期已经结束
int number = 5;
return number;
}
//不要轻易返回一个堆空间变量的引用
//除非已经有了内存回收的策略
int & func3()
{
int * p3 = new int(100);
return *p3;
}
void test2()
{
int idx = 0;
cout << "array[idx] = " << getNumber(idx) << endl;
&getNumber(1);
getNumber(idx) = 10;//左值:就是可以取地址
cout << "array[idx] = " << getNumber(idx) << endl;
//&func1();//error 右值(临时变量): 不可以取地址
//func1() = 100;//error
//int & ref = func2();
//cout << "ref = " << ref << endl;//error
int & ref2 = func3();
cout << "ref2 = " << ref2 << endl;
delete &ref2;
int a = 3, b = 4, c = 5;
c = a + func3() + b + c;//执行该表达式之后,就会发生内存泄漏
cout << "c = " << c << endl;
}
int main(void)
{
test0();
test1();
test2();
return 0;
}
external C 编译
//对于C源码不希望按C++方式进行调用(不进行名字改编),
// 按C的方式进行调用
//
//
//C的代码要放在C++中运行(C与C++混合编程)
#ifdef __cplusplus //该宏只会在C++的编译器中定义
extern "C"
{ //都会用C的方式进行调用
#endif
int add(int x, int y)
{
return x + y;
}
#ifdef __cplusplus
}//end of extern "C"
#endif
简答题:
1. const关键字与宏定义的区别是什么?
2. 引用于指针的区别是什么?
3. malloc的底层实现是怎样的?free是怎么回收内存的?
4. new/delete与malloc/free的区别是什么?
inline函数
//inline函数的功能与带参数的宏定义效果一样
//尽量用inline函数替换带参数的宏定义
#pragma once
//inline函数可以有声明和实现,但是必须在同一文件
//inline函数不能分成头文件和实现文件
inline int add(int x, int y)
{ //一般不要放循环语句
return x + y;
}
//......
C++ 类型转换
#include <iostream>
using std::cout;
using std::endl;
void test0()
{
double val1 = 1.11;
int val2 = (int)val1;
int val3 = int(val1);
cout << "val2 = " << val2 << endl;
cout << "val3 = " << val3 << endl;
}
void test1()
{
double val1 = 1.11;
int val2 = static_cast<int>(val1);
cout << "val2 = " << val2 << endl;
int * p1 = static_cast<int*>(malloc(sizeof(int)));
*p1 = 10;
free(p1);
//const_cast/dynamic_cast/reinterpret_cast
}
int main(void)
{
//test0();
test1();
return 0;
}
struct 在C++中
struct Example
{
//struct的默认访问权限是Public
Example(int x)
: _ix(_iy)
, _iy(x)
{
}
void setX(int ix)
{ _ix = ix; }
void setY(int iy)
{ _iy = iy; }
void print()
{
cout << "(" << _ix << "," << _iy << ")" << endl;
}
private:
int _ix;
int _iy;
};
C++中的string
#include <string.h>
#include <string>
#include <iostream>
using std::cout;
using std::endl;
void test0()
{
const char * pstr1 = "hello";// 文字常量区, 只读的
const char * pstr2 = "world";//C风格字符串
//*pstr1 = 'X';//error 不能进行修改
//空间是否足够
char * ptmp = new char[strlen(pstr1) + strlen(pstr2) + 1]();
strcpy(ptmp, pstr1);
strcat(ptmp, pstr2);
cout << "ptmp = " << ptmp << endl;
delete [] ptmp;
//获取字符串的长度
//strlen ==> 时间复杂度是O(N)
}
void test1()
{
//把C风格字符串转换成C++风格的字符串
std::string s1 = "hello";
std::string s2 = "world";
//做字符串的拼接
std::string s3 = s1 + s2;
cout << "s3 = " << s3 << endl;
s3 += "aaa";
cout << "s3 = " << s3 << endl;
//获取字符串的长度
cout << "s3'size = " << s3.size() << endl;
cout << "s3'length = " << s3.length() << endl;
//把C++风格字符串转换成C风格字符串
const char * pstr = s3.c_str();
const char * pstr2 = s3.data();
cout << "pstr = " << pstr << endl;
cout << "pstr2 = " << pstr2 << endl;
//遍历元素
for(size_t idx = 0; idx != s3.size(); ++idx)
{
cout << s3[idx] << endl;//数组的下标进行访问
}
//&s3; 代表的是std::string类型的对象的首地址
s3.append(3, 'j');
cout << "s3 = " << s3 << endl;
s3.append(s1);
cout << "s3 = " << s3 << endl;
s3.append("wangdao");
cout << "s3 = " << s3 << endl;
s3.append("shenzhen", 4);
cout << "s3 = " << s3 << endl;
//查找元素的用法
size_t pos = s3.find("world");
cout << "pos = " << pos << endl;
std::string s4 = s3.substr(pos, 5);
cout << "s4 = " << s4 << endl;
}
int main(void)
{
test0();
test1();
return 0;
}
构造函数和析构函数
#include <iostream>
using std::cout;
using std::endl;
class Point
{
public:
#if 1
//当没有显式定义构造函数时,系统会自动提供一个
//默认构造函数
Point()
{
_ix = 0;
_iy = 0;
cout << "Point()" << endl;
}
#endif
#if 1
//构造函数可以重载
//
//如果显式定义了有参构造函数时,系统就
//不会再自动提供默认构造函数
//
//如果还希望通过默认构造函数创建对象,则必须
//显式提供一个默认构造函数
//
Point(int ix, int iy)
{
_ix = ix;
_iy = iy;
cout << "Point(int,int)" << endl;
}
Point(int ix = 0, int iy = 0)
: _ix(ix) //对于数据成员的初始化,都要放在初始化表达式之中进行
, _iy(iy)
{
//_ix = ix;//赋值语句
//_iy = iy;
cout << "Point(int=0,int=0)" << endl;
}
#endif
void print()
{
cout << "(" << _ix
<< "," <<_iy
<< ")" << endl;
}
// 析构函数没有返回值,函数名与类名相同,同时还需要在前面
// 加上一个波浪号,与构造函数进行区分;
// 析构函数没有形参, 析构函数只有一个
//
// 当一个对象的生命周期结束的时候,会自动调用析构函数
//
//系统提供的析构函数
~Point()
{}
#if 0
~Point()
{
cout << "~Point()" << endl;
}
#endif
private:
int _ix;
int _iy;
};
int main(void)
{
//Point pt1(1, 2);
//pt1.print();
Point pt2;//调用无参(默认)构造函数
pt2.print();
return 0;
}
#include <iostream>
using std::cout;
using std::endl;
class Point
{
public:
Point(int ix = 0, int iy = 0)
: _ix(ix)
, _iy(iy)
{
cout << "Point(int=0,int=0)" << endl;
}
//系统提供的复制构造函数
//问题1: 形参中的引用符号不能删除,如果删除,
//会造成复制构造函数无穷递归调用,直到栈溢出,程序崩溃
//
//问题2: 形参中的const关键字可以去掉不?
// 不可以去掉,如果去掉,当传递过来的参数是右值时,就会
// 报错
//Point(const Point rhs):
//Point(Point & rhs)
Point(const Point & rhs) //复制构造函数形式是固定的
: _ix(rhs._ix)
, _iy(rhs._iy)
{
cout << "Point(const Point & )" << endl;
}
void print()
{
cout << "(" << _ix
<< "," <<_iy
<< ")" << endl;
}
private:
int _ix;
int _iy;
};
//形参与实参都是对象(值传递),会调用复制构造函数
void func1(Point pt)
{
cout << "pt = ";
pt.print();
}
//函数的返回值是对象时,执行return语句
//也会调用复制构造函数;但一般情况下,会
//被编译器优化掉;如要要看到完整形式,需要
//在编译时 加上编译选项 -fno-elide-constructors
Point func2()
{
Point pt(11, 12);
cout << ">> pt = ";
pt.print();
return pt;
}
int main(void)
{
int a = 3;
int b = a;
//int & ref = 1;//字面值常量, 非const引用不能绑定到右值
const int & ref2 = 1;// const引用可以绑定到右值
cout << "ref2 = " << ref2 << endl;
Point pt1(1, 2);
cout << "pt1 = ";
pt1.print();
//Point(const Point rhs);
// const Point rhs = pt1;
// Point(const Point rhs);
// const Point rhs = pt1;
Point pt2 = pt1;//调用复制(拷贝)构造函数
cout << "pt2 = ";
pt2.print();
cout << "----" << endl << endl;
func1(pt2);
cout << "----" << endl << endl;
func2();//临时对象, 右值
Point pt3 = func2();//Point(Point & rhs);
//非const引用无法绑定到右值
//Point & rhs = func2();
pt3.print();
cout << "----" << endl << endl;
func2().print();
return 0;
}
程序内存区域
#include <stdio.h>
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
int a = 1;//全局静态区
char * p1;//全局变量如果没有显式进行初始化,
//系统也会设置默认值
int test0(void)
{
int b = 2;
const char * p2 = "hello,world";//文字常量区
char str[] = "hello,world";//栈区
//str = 0x1000;//str 常量
char * p3;//野指针
p3 = new char[20]();//堆区
strcpy(p3, "hello,world");
static int c = 100;//局部静态变量, 全局/静态区
++c;
printf("sizeof(str) = %lu
", sizeof(str));
printf("sizeof(p2) = %lu
", strlen(p2));
printf("&a = %p
", &a);
printf("&b = %p
", &b);
printf("p1 = %p
", p1);
printf("&p1 = %p
", &p1);
printf("p2 = %p
", p2);
printf("&p2 = %p
", &p2);
printf("str = %p
", str);
printf("p3 = %p
", p3);
//*p3 = '1';//error 野指针
printf("&p3 = %p
", &p3);
printf("&c = %p
", &c);
printf("address(hello,world) = %p
", "hello,world");
printf("address(test0) = %p
", &test0);//程序代码区
return 0;
}
int main(void)
{
test0();
return 0;
}
默认参数
#include <stdio.h>
#include <iostream>
using std::cout;
using std::endl;
//默认参数/缺省参数
//一旦某个参数被设置了默认值,后面的所有的参数都要设置默认值
//
//默认参数的设置只能从右到左的顺序进行
int add(int x = 0, int y = 0)
{
return x + y;
}
int add(int x, int y, int z)
{
return x + y + z;
}
int main(void)
{
int a = 3, b = 4, c = 5;
cout << "add(a) = " << add(a) << endl;
cout << "add(a, b) = " << add(a, b) << endl;
cout << "add(a, b, c) = " << add(a, b, c) << endl;
return 0;
}
拷贝构造函数 构造函数析构函数 深拷贝浅拷贝
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
//代码风格是非常重要的
//如果是自定义类类型,都要大写首字母
class Computer
{
public://public成员可以在类之外访问
//public成员称为类对外的接口、功能、服务
//对成员函数采用驼峰方式进行命名
void setBrand(const char * brand)
{
strcpy(_brand, brand);
}
void setPrice(double price)
{
_price = price;
}
//protected://保护成员不能在类之外访问
void print()
{
cout << "brand:" << _brand << endl
<< "price:" << _price << endl;
}
//私有成员尽量放到类的底部
private://私有的成员不能在类之外访问
char _brand[20];//brand_ / m_brand
double _price;
};
int main(void)
{
int a;
Computer pc1;
pc1.setBrand("Thinkpad");
pc1.setPrice(8888);
pc1.print();
//pc1._price = 1000;//error
return 0;
}
//类的声明
class Computer
{//class的默认访问权限是private的
public:
void setBrand(const char * brand);
void setPrice(double price);
void print();
//通过private关键字表现封装的特性
private://类对象占据的空间只与数据成员有关,
//成员函数存储在程序代码区, 不会占据对象的空间
char _brand[20];
double _price;
};
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
class Computer
{
public:
Computer(const char * brand, double price)
: _brand(new char[strlen(brand) + 1]()) //浅拷贝, 只传地址
, _price(price)
{
strcpy(_brand, brand);
cout << "Computer(const char *, double)" << endl;
}
void print()
{
cout << " brand:" << _brand << endl
<< " price:" << _price << endl;
}
//对象销毁的过程中会自动调用析构函数
//
//问题: 执行了析构函数就是销毁了对象
#if 0
void release()
{
delete [] _brand;
}
#endif
~Computer()
{ //析构函数的作用:是用来回收对象申请的资源
if(_brand) {
delete [] _brand;
_brand = nullptr;//NULL
}
cout << "~Computer()" << endl;
}
private:
char * _brand;
double _price;
};
//Computer pc2("Xiaomi", 7500);
int test0(void)
{
{
Computer pc1("MateBook", 6666);
cout << ">> pc1: " << endl;
pc1.print();
}
cout << ">> pc2: " << endl;
// pc2.print();
//堆空间的对象的销毁,需要手动执行
Computer * pc3 = new Computer("Thinkpad", 7777);
cout << ">> pc3: " << endl;
pc3->print();
delete pc3;//不要忘了, 执行delete表达式的过程中,就会调用析构函数
static Computer pc4("Macbook pro", 20000);
cout << ">> pc4: " << endl;
pc4.print();
return 0;
}
void test1()
{
Computer * pc3 = new Computer("Thinkpad", 7777);
cout << ">> pc3: " << endl;
pc3->print();
pc3->~Computer();//该语句被执行之后,对象是没有被销毁的
delete pc3;
}
void test2()
{
Computer pc1("MateBook", 6666);
cout << ">> pc1: " << endl;
pc1.print();
pc1.~Computer();//析构函数可以主动调用,但不推荐这样做
//析构函数应该让其自动执行
//pc1.release();
}
int main(void)
{
//test1();
test2();
return 0;
}
//系统提供的 已经不能满足需求
#if 0
Computer(const Computer & rhs)
: _brand(rhs._brand)//浅拷贝, 只传地址
, _price(rhs._price)
{
cout << "Computer(const Computer&)" << endl;
}
#endif
Computer(const Computer & rhs)
: _brand(new char[strlen(rhs._brand) + 1]())
, _price(rhs._price)
{
strcpy(_brand, rhs._brand);
cout << "Computer(const Computer&)" << endl;
}