文件转换顺序及过程
以hello.c为例:
- hello.c经过预处理器,将宏展开,生成文件hello.i
- hello.i经过编译器,将文件编译成汇编语言,生成文件hello.s
- hello.s经过汇编器,将文件编译成目标文件hello.o(hello.obj)
- hello.o经过链接器,将文件编译成可执行文件
进制输出
oct:8进制 dec:10进制 hex:16进制
boolalpha
函数名称,功能是把bool值显示为true或false
数组长度
int arr[] = {2, 6, 3, 7, 5, 2};
int len = sizeof(arr) / sizeof(int);
防止头文件被重复包含
//第一张方法
#ifndef XXX_H //XXX表示头文件名的全大写,ctrl+shift+U :改为大写
#define XXX_H
...
#endif
//第二种方法
#pragma once //c++特性
函数参数默认值
有默认参数值的参数必须放在参数表的最右端,如下:
void fun(int i; int j = 3; int k = 5);//正确
void fun(int i; int j = 3; int k);//错误
无实参则用默认值,否则实参覆盖默认值,如下:
#include<iostream>
using namespace std;
void fun(int i, int j = 3, int k = 5);
int main() {
fun(10);
fun(10, 20);
fun(10, 20, 30);
return 0;
}
void fun(int i, int j, int k) {
cout << i << "," << j << "," << k << endl;
}
/*
输出结果
10,3,5
10,20,5
10,20,30
*/
内联函数
编译时将函数体代码和实参代替函数调用语句(节省时间);
内联函数关键字:inline
#include<iostream>
using namespace std;
inline int max(int a, int b, int c);//判断最大值,不考虑相等情况
int main() {
int i = 10, j = 20, k = 30, m;
m = max(i, j, k);//代替为 int a, b, c; if (b > a) a = b; if (c > a) a = c; 内联方式
cout << "max=" << m << endl;
return 0;
}
inline int max(int a, int b, int c) {
if (b > a) a = b;
if (c > a) a = c;
return a;
}
思考:为什么不所有的函数都使用内联方式呢?
1.内联是建议性的,最后还是由编译器决定;
2.逻辑简单、调用频繁的函数建议使用内联;
3.递归函数无法使用内联方式。
内存的申请(new)和释放(delete)
int *p = new int;//申请内存
delete p;//释放内存
int *arr = new int[10];//申请块内存
delete []arr;//释放块内存
int *p = new int;//块内存注意匹配 int *p = new int[1000];
if(NULL == p){
//内存分配失败
//异常处理
}
delete p;//块内存delete []p;
p = NULL;//不管是否分配成功都要操作
构造函数和析构函数
类名();//构造函数定义结构
~类名();//析构函数定义结构
构造函数
构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。构造函数可用于为某些成员变量设置初始值。默认的构造函数没有任何参数,但如果需要,构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值。
#include <iostream>
using namespace std;
class Line {
public:
void setLength( double len );
double getLength( void );
Line(double len); // 这是构造函数
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line( double len) {
cout << "Object is being created, length = " << len << endl;
length = len;
}
void Line::setLength( double len ) {
length = len;
}
double Line::getLength( void ) {
return length;
}
// 程序的主函数
int main( ) {
Line line(10.0); // 获取默认设置的长度
cout << "Length of line : " << line.getLength() <<endl;
// 再次设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
//当上面的代码被编译和执行时,它会产生下列结果:
//Object is being created, length = 10
//Length of line : 10
//Length of line : 6
使用初始化列表来初始化字段:
Line::Line( double len): length(len)
{
cout << "Object is being created, length = " << len << endl;
}
上面的语法等同于如下语法:
Line::Line( double len)
{
length = len;
cout << "Object is being created, length = " << len << endl;
}
假设有一个类 C,具有多个字段 X、Y、Z 等需要进行初始化,同理地,您可以使用上面的语法,只需要在不同的字段使用逗号进行分隔,如下所示:
C::C( double a, double b, double c): X(a), Y(b), Z(c)
{
....
}
析构函数
类的析构函数 是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。
下面的实例有助于更好地理解析构函数的概念:
实例
#include <iostream>
using namespace std;
class Line
{
public:
void setLength( double len );
double getLength( void );
Line(); // 这是构造函数声明
~Line(); // 这是析构函数声明
private:
double length;
};
// 成员函数定义,包括构造函数
Line::Line(void)
{
cout << "Object is being created" << endl;
}
Line::~Line(void)
{
cout << "Object is being deleted" << endl;
}
void Line::setLength( double len )
{
length = len;
}
double Line::getLength( void )
{
return length;
}
// 程序的主函数
int main( )
{
Line line;
// 设置长度
line.setLength(6.0);
cout << "Length of line : " << line.getLength() <<endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Object is being created
Length of line : 6
Object is being deleted
深拷贝和浅拷贝
我们首先要知道默认拷贝构造函数可以完成对象的数据成员简单的复制,这也称为浅拷贝。对象的数据资源是由指针指向的堆时,默认的拷贝构造函数只是将指针复制。
a. 我们首先分析下面的代码为什么会出错??
class Test
{
private:
int* p;
public:
Test(int x)
{
this->p=new int(x);
cout << "对象被创建" << endl;
}
~Test()
{
if (p != NULL)
{
delete p;
}
cout << "对象被释放" << endl;
}
int getX() { return *p; }
};
int main()
{
Test a(10);
//会调用默认的拷贝构造函数
Test b = a;
return 0;
}
我们画一个示意图分析:
看懂了示意图,我们知道为什么会出错了(同一个内存被释放两次)。我们接下来分析解决方案:
class Test
{
private:
int* p;
public:
Test(int x)
{
this->p=new int(x);
cout << "对象被创建" << endl;
}
~Test()
{
if (p != NULL)
{
delete p;
}
cout << "对象被释放" << endl;
}
int getX() { return *p; }
//深拷贝(拷贝构造函数)
Test(const Test& a)
{
this->p = new int(*a.p);
cout << "对象被创建" << endl;
}
//浅拷贝(拷贝构造函数)
//Test(const Test& a)
//{
// this->p = a.p;
// cout << "对象被创建" << endl;
//}
};
int main()
{
Test a(10);
//我们手动的写拷贝构造函数,C++编译器会调用我们手动写的
Test b = a;
return 0;
}
virtual关键字
virtual可以修饰成员函数、析构函数,不可以修饰普通函数、全局函数、静态成员函数、内联函数、构造函数
指针
#include <stdio.h>
int main(){
int num = 99;
int *p = #
int *q = p;
printf("%p\n",p);
printf("%p\n",q);
printf("%p\n",&p);
printf("%p\n",&q);
printf("%d\n",*p);
printf("%d\n",*q);
return 0;
}
代码和输出结果如上。
- 定义一个变量num,值为99,内存中存放的地址为000000000062FE1C,意思是说000000000062FE1C这个地址里面装的是99。
- 然后定义一个p指针指向num,根据输出结果知道p的内存地址为000000000062FE10,指针p的含义是这个000000000062FE10地址存放的是num这个数的地址,也就是000000000062EE1C。想要取出p指针指向地址的值,就用*p,*p取出000000000062EE1C地址存放数据的值,也就是99。
- 再定义一个指针q,指向p指针,意思是,你p指向谁我q就指向谁,根据输出结果,得知q的地址是000000000062FE08,000000000062FE08这个地址里面装的是99的地址,也就是000000000062FE1C。
此时在各个变量内存中的布局已经解释清楚了。接下来的输出就简单了:
想要输出的值 | 解释 | 结果 |
---|---|---|
&num | 输出num的内存地址 | 000000000062FE1C |
num | num的值 | 99 |
&p | p的内存地址 | 000000000062FE10 |
*p | p指向地址的内容 | 99 |
&q | q的地址 | 000000000062FE08 |
*q | q指向地址的内容 | 99 |
注:本文是作者学习c++时的一些笔记,有少部分内容是从网上直接拷贝的,侵删;
小白一枚,错误请指正,谢谢
附上本文md文件下载地址:https://download.csdn.net/download/weixin_41533956/11502300