1.#inclue
#inclue<iostream>中的 # 是预处理标志,要最先处理,即在编译器编译代码之前运行
#inclue<> 和 #include “” 的区别:
< >引用的是编译器的类库路径里面的头文件
" "引用的是你程序目录的相对路径中的头文件
< >一般是引用自带的一些头文件:stdio.h、conio.h、string.h、stdlib.h等等之类的
" "一般是用来引用自己写的一些头文件
如果使用" ",会先在你项目的当前目录查找是否有对应头文件
如果没有,它还是会在对应的类库路径目录里面查找对应的头文件
2.iostream是一个标准库,里面包含了cin和cout,用法就是std::cout<<"hello";
int x;
std:cin>>x;
还可以这样:
int a,b,c;
std::cin>>a>>b>>c;
std::cout<<a<<" "<<b<<"
"<<c<<"ddd";
"
"代表换行,和std:endl是一个意思,但是后者还会刷新缓存区,两者基本一样
3.std是标准的意思。
namespace是命名空间。
<iostream>是头文件。//注意iostream.h
cout,cin,endl是在std命名空间中定义的对象。
4.C++中cout定义在命名空间std中,因此,在使用cout时可以用std::cout这样的方式使用,需要注意的是,cout是ostream类型的对象,而ostream类型定义在iostream头文件中,因此,在使用cout时,还需要使用#include<iostream>. 总结:C++中的命名空间和C#中的意思基本一样,也是相同的作用,用法也差不多,C++中的std是包含了所有的标识符的命名空间,所以一般都要using std,当然也可以直接std::cout 这样调用。
5.system("pause");是暂停的意思,在main函数最后可以不让控制台关闭,用Ctrl+F5也不会关闭,F5调试的话又不加system("pause")的话就会自动关闭。
6.#include "stdafx.h" ???
7.#include<iostream>和<iostream.h>的区别:
#include<iostream>中的iostream是C++标准头文件库,而#include<iostream.h>是C中的头文件库,因为C++继承了C的特性,所以也保留了iostream.h这种写法,因为C++引入了namespace,所以用iostream的时候就要开辟命名空间,例如:
#include<iostream>
using namespace std;
用iostream.h的时候就不用命名空间;
8.C++的编译器是从上到下的,例如这段代码
int _tmain()
{
std::cout <<add(6,9);
return 0;
}
int add(int x, int y)
{
return x + y;
}
编译器会报错,但是把add放在上面就不会出错,C++的函数可以不用声明直接定义,但是这不是一个好习惯,最好先声明,原因很简单,声明后编译器就知道这个函数的存在了。声明的时候没有给函数分配内存,而定义就会函数分配内存
9.class类的成员函数的声明和定义不建议全部放在类内,最好定义要放在外面,要加上域符号::
为什么要声明和定义分开?因为写在一起就比如void p(){std::cout“111”;};
这样一个函数其实被称为内联函数,就是相当于把函数实现部分拷贝到调用函数的地方,但是一旦{}里面的代码很大,拷贝就会浪费性能,所以声明和定义最好分开,这样就不会去拷贝了
10.头文件用<>包括表示绝对路径,用""表示相对路径,include头文件就可以认为cpp文件包含了头文件中的所有代码。
为什么要用头文件的原因:
(1).可以被多个cpp文件同事引用,(2).类的使用者不关心类的细节,只要阅读头文件就可以知道所有信息
11.析构函数和构造函数都不能有返回值,析构函数不能带参数,一个类只能有一个析构函数,而构造函数可以重载,可以带参数。
12.析构函数会在程序结束时执行
13.int *p; p = 0; 第二句是将p变为空指针; 这样更安全
14.double *p;这是一个double类型的指针,所以这个指针的内存为8个字节,
int *q 这是一个int类型的指针,内存为4字节,而且两者不能相互转换
指针的类型必须与变量的类型相匹配,不然会报错
15.int a = 1;
int* p; //int类型指针
p = &a;
cout << *p<<endl; //取指针代表的地址的值,即a的值为1
cout << p << endl;; //指针p的值 为0057fe10
cout << &p<<endl; //指针的地址 0057fe04
cout << &a << endl; //a变量的地址,即p的值 0057fe10
16.typedef 的意思就是把。。替换成。。;
double a=9.0 ; double *p=&a; *p就代表去取指针存放的地址所指向的值,即*p=9.0;
17.为要使用指针:
1.处理堆中存放的大型数据
2.快速访问类的成员数据和函数
3.以别名的方式向函数传递参数
18.指针*号靠近变量还是靠近类型:
int *a;
int* a;
首先这两种其实是一样的,无伤大雅,但更建议使用第一种,靠近变量
两者意思相同且后者看上去更为清楚:a被声明为类型为 int* 的指针. 但是,这并不是一个好技巧,原因如下:
int* b, c, d;
人们很自然地以为这条语句把所有三个变量声明为指向整形的指针, 但事实上并非如此. 我们被它的形式愚弄了. 星号实际上是表达式 *b 的一部分, 只对这个标识符有用. b 是一个指针, 但其余两个变量只是普通的整形. 要声明三个指针, 正确的语句如下:
int *b, *c, *d;
19.引用&
int &ra=a; 引用就是别名,就象库里的小名叫小学生,两者都是指库里
C++之所以增加引用类型, 主要是把它作为函数参数,以扩充函数传递数据的功能。
引用就是常量,只能进行初始化,不能赋值 即int &ra=a;而不能写成 int &ra;ra=a;
引用也可以是类的引用,即代表该类的别名
引用的自身的地址和本尊是一样的,只能修改其保存的值,而不能修改本身
引用很容易与指针混淆,它们之间有三个主要的不同:
1.不存在空引用。引用必须连接到一块合法的内存。
2.一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。
3.引用必须在创建时被初始化。指针可以在任何时间被初始化。
20.传参三种方式:
int a=3;int b=4;
(1)按值传递 void swap(int a,int b);传进去之后不改变原来函数外a和b的值,跟C#一样,按值传递会在函数内部创建一个对象的副本,返回的也是一个对象的副本,所以外面的值不会改变.
(2)按地址传递 void swap(int *a,int *b);传入后会改变原来a和b的值
(3)按引用(别名)传递 void swap(int &a,int &b);传入后会改变原来a和b的值,这个&就相当于C#中的ref
尽量使用按别名传递的方式,这样能解决创建副本的问题,也能节省性能
下面的例子,会输出
a=11 b=22
a=22 b=11
a=22 b=11
#include<iostream>
void swap0(int a, int b) //按值传递
{
int temp = a;
a = b;
b = a;
}
void swap1(int& a, int& b)//按引用传递
{
int temp = a;
a = b;
b = temp;
}
void swap2(int* a, int *b)//按地址传递
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 11; int b = 22;
swap0(a, b);
std::cout << "a=" << a << " " << "b=" << b << std::endl;
a = 11; b = 22;
swap1(a, b);
std::cout << "a=" << a << " " << "b=" << b << std::endl;
a = 11; b = 22;
swap2(&a, &b);
std::cout << "a=" << a << " " << "b=" << b << std::endl;
return 0;
}
21.c++为什么要产生副本?通过汇编理解指针,学习汇编。。
22.栈区和堆区
1.栈区:由编译器自动分配并且自动释放,一般是函数参数和局部变量
2.堆区:一般有程序员分配释放,若不释放,则程序结束时由系统释放
堆是有一串不连续的链表串联起来的内存,速度较慢,容易产生内存碎片,容易使用,在申请内存的时候,系统会遍历一个空闲地址的链表,找到合适的地址分配给使用者
栈是系统自动分配的,程序员不能操作,而且一般较小,一般是函数参数或者局部变量,当函数结束时会释放,栈中的内存是先进后出的。一般第一个进栈的是函数下一行的内存地址,然后从右到左的函数参数,然后是变量,出栈时完全相反,所有变量出栈之后就剩函数下一行的内存地址了,然后自动跳转到下一行执行!
23.内存的三种分配方式:
1. 从静态存储区分配:此时的内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在。全局变量,static变量等在此存储。
2. 在栈区分配:相关代码执行时创建,执行结束时被自动释放。局部变量在此存储。栈内存分配运算内置于处理器的指令集中,效率高,但容量有限。
3. 在堆区分配:动态分配内存。用new/malloc时开辟,delete/free时释放。生存期由用户指定,灵活。但有内存泄露等问题。
24.类的初始化,用new和不用new的区别:
通过下面这个例子,会输出“释放1”,我们可以得出在不使用new创建对象时,对象的内存空间是在栈中的,其作用范围只是在函数内部,函数执行完成后就会调用析构函数,删除该对象。而使用new创建对象是创建在堆中的,必须要程序员手动的去管理该对象的内存空间
#include<iostream>
class TestNew
{
public:
TestNew(int ID);
~TestNew();
private:
int ID;
};
TestNew::TestNew(int ID)
{
this->ID = ID;//this就是TestNew类
}
TestNew::~TestNew()
{
std::cout << "释放" << this->ID << std::endl;
}
void Test()
{
TestNew test(1);
TestNew * ptest = new TestNew(2);
}
int main()
{
Test();
return 0;
}
25.int *p=new int; 在堆中new一个int型 即4字节的内存空间,内存地址保存在p中
*p=3, 修改这块内存中存放的值,改为3
delete p; 释放这块内存空间,但是并没有释放p,p指针还能继续使用,
p=0; 一般是将p清0,以免错误
p=new int; p还能继续使用
*p=400; 修改这块内存中存放的值,改为400
26.内存泄漏:因为new在堆中的内存不会自动释放,假如保存这块内存的指针是一个函数中的局部变量,当函数执行完毕后,会释放这个指针,那么这块内存就象消失了一样,再也找不回来了,这就叫内存泄漏,必须对这个指针使用delete
27.常见内存错误及对策
1. 内存分配未成功,却被使用。
对策:使用内存之前检查是否分配成功。用p!=NULL判断。
2. 内存分配成功,未初始化就被使用。
内存的缺省值没有统一的标准。大部分编译器以0作为初始值,但不完全是。
对策:内存初始化时赋初值。
3. 内存操作越界。
对策:只能是小心了。
4. 释放了内存,仍然使用。
(1) 使用显示delete和free的野指针。
对策:释放完内存,将指针置为NULL。
(2) 使用隐式delete和free的野指针。主要是指函数返回指向栈内存的指针或引用。
对策:当然是不要返回就可以了。
5. 未释放内存,导致内存泄露。
用new/malloc开辟了内存,没用delete/free释放.
对策:new和delete的个数一定相同;malloc和free的个数一定相同;new[]和[]delete一定对应
28.指针能加减能比较大小
29.将指针定义为常量指针 int *const p=&a; p是一个常量指针,p本身的值无法修改,但是它指向的地址可以修改
30.假如把const放在前面 const int *p=&a; p是指向常量的指针,p本身可以修改,但是p所指向的目标不能修改
31.函数的成员变量的初始化
class A
{
public:
A():x(4),y(7) //这是一种成员变量的初始化写法,会输出28
{
cout<<x*y
};
private:
int x;
int y;
}
如果把x定义为const的话,就只能使用这种写法给成员变量初始化,因为常量和引用只能被初始化不能被赋值。