1、程序的内存模型
①代码区:存放函数体的二进制代码,由操作系统进行管理
②全局区:存放全局变量和静态变量以及常量
③栈区:由编译器自动分配和释放,存放函数的参数值、局部变量等。
④堆区:由程序员分配和释放,若程序员不释放,程序结束时有操作系统回收。
①代码区:在运行程序之前就已经存在。特点是共享和只读。
②全局区:在运行程序之前就已经存在。
#include "iostream"
#include "string"
using namespace std;
//全局变量
int g_a;
//全局常量
const int c_g_a = 10;
//全局字符常量
const string c_str = "hello";
int main()
{
//局部变量
int l_a;
//局部常量
const int c_l_a = 10;
//局部字符常量
const string c_l_str = "hello";
//静态变量
static int s_l_a;
//全局区
cout << "全局变量地址为:" << (int)&g_a << endl;
cout << "全局常量地址为:" << (int)&c_g_a << endl;
cout << "全局字符常量地址为:" << (int)&c_str << endl;
cout << "静态变量地址为:" << (int)&s_l_a << endl;
//非全局区
cout << "局部变量地址为:" << (int)&l_a << endl;
cout << "局部常量地址为:" << (int)&c_l_a << endl;
system("pause");
return 0;
}
③栈区
注意不要放回局部变量的地址。
#include "iostream"
#include "string"
using namespace std;
int * aa(void)
{
int a = 10;
return &a;
}
int main()
{
int *p = aa();
cout << *p << endl;//编译器做了一次保留
cout << *p << endl;//不是10是因为a的地址在调用完函数后已经被编译器释放了
system("pause");
return 0;
}
④堆区
#include "iostream"
#include "string"
using namespace std;
int * aa(void)
{
int *a = new int(10);
return a;
}
int main()
{
int *p = aa();
//由于是用户自己建立了一个堆区,所以并没有释放
cout << *p << endl;
cout << *p << endl;
system("pause");
return 0;
}
2、堆栈开辟变量和数组以及空间的释放
#include "iostream"
#include "string"
using namespace std;
int main()
{
//在堆区定义一个变量
int *p = new int(10);
cout << *p << endl;
//在堆区定义一个数组
int *arr = new int[10];
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
for (int i = 0; i < 10; i++)
{
cout << arr[i] << " ";
}
cout << endl;
delete p;//变量释放方法
delete[] arr;//数组释放方法
system("pause");
return 0;
}
3、引用
引用必须初始化,而且初始化后不能再更改,即不能改变指向。
#include "iostream"
#include "string"
using namespace std;
int main()
{
int a = 10;
//引用的基本语法
int &b = a;//b和a指向同一块地址
cout << "a= " << a << endl;
cout << "b= " << b << endl;
b = 20;
cout << "a= " << a << endl;
cout << "b= " << b << endl;
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
void swap(int &a, int &b);
int main()
{
//引用作为函数参数
int a = 10;
int b = 20;
cout << "交换前:" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
swap(a, b);
cout << "交换后:" << endl;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
system("pause");
return 0;
}
void swap(int &a,int &b)
{
int temp = a;
a = b;
b = temp;
}
#include "iostream"
#include "string"
using namespace std;
int& test1(void);
int& test2(void);
int main()
{
//1、不能返回局部变量的引用
//2、返回引用的函数可以作为左值
int &b = test1();
cout << "b= "<< b << endl;//编译器做了一次保留
cout << "b= "<< b << endl;//打印错误,因为a是局部变量
int &c = test2();
cout << "c= " << c << endl;
cout << "c= " << c << endl;
system("pause");
return 0;
}
int& test1(void)
{
int a = 10;
return a;
}
int& test2(void)
{
static int a = 10;
return a;
}
引用其实就是指针常量
int &a = b等价于int * const a = &b;
#include "iostream"
#include "string"
using namespace std;
int main()
{
int &a = 10;//错误
const int &a = 10;//正确,即常量引用,相当于int temp=10;const int &a=temp;
system("pause");
return 0;
}
常量引用经常用于函数的形参,即防止误操作对数据造成损坏。
eg:
void datashow(const int & a)
{
cout<<a<<endl;
}
4、函数默认参数
#include "iostream"
#include "string"
using namespace std;
int add1(int a, int b=10, int c=20);
int main()
{
//函数的默认参数
cout << add1(10) << endl;
//注意事项:
//1、有函数申明和函数体时,只能函数申明给出 默认参数
//2、参数从第一个给出默认参数值开始,后面的参数都需要给出默认参数值
system("pause");
return 0;
}
int add1(int a, int b, int c)
{
return a + b + c;
}
5、占位参数
#include "iostream"
#include "string"
using namespace std;
int add1(int a, int b = 10, int = 10);//占位参数也可以有默认参数值
int main()
{
//函数的默认参数
cout << add1(10) << endl;
//注意事项:
//1、有函数申明和函数体时,只能函数申明给出 默认参数
//2、参数从第一个给出默认参数值开始,后面的参数都需要给出默认参数值
system("pause");
return 0;
}
int add1(int a, int b, int)//第三个参数为占位参数
{
return a + b;
}
6、函数重载
函数重载的条件:
①两个函数的作用域相同
②两个函数的函数名相同
③两个函数的形参类型不同或者个数不同或者顺序不同
#include "iostream"
#include "string"
using namespace std;
void function()
{
cout << "function1()" << endl;
}
void function(int a)
{
cout << "function1(int a)" << endl;
}
void function(int a, int b)
{
cout << "function1(int a, int b)" << endl;
}
void function(int a, double b)
{
cout << "function1(int a, double b)" << endl;
}
void function(double a, int b)
{
cout << "function1(double a, int b)" << endl;
}
int main()
{
function();
function(1);
function(1, 1);
function(1, 0.1);
function(0.1, 1);
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
void function(int &a)
{
cout << "function1(int a)" << endl;
}
void function(const int &a)
{
cout << "function1(const int &a)" << endl;
}
int main()
{
//函数重载遇到引用
int a = 10;
function(10);
function(a);
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
void function(const int &a)
{
cout << "function1(const int &a)" << endl;
}
void function(int a, int b=10)
{
cout << "function1(int a, int b)" << endl;
}
int main()
{
//函数重载遇到默认值
function(1);//错误,因为void function1(const int &a)与void function(int a, int b=10)产生了歧义
system("pause");
return 0;
}
7、类
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
//类
class Circle
{
//访问权限
public:
//属性
double r;//半径
//行为
double cacluate_c(double a)
{
return 2 * PI*r;
}
};
int main()
{
Circle a;
cout << "请输入圆的半径:";
cin >> a.r;
cout << "圆的周长为:" << a.cacluate_c(a.r) << endl;
system("pause");
return 0;
}
类中的属性也叫做成员属性、成员变量
类中的行为也叫做成员函数、成员方法
8、访问权限
①公共权限:public,类内可以访问,类外也可以访问
②保护权限:protected,类内可以访问,类外不可以访问,子类可以访问
③隐私权限:private,类内可以访问,类外不可以访问,子类不可以访问
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
class Person
{
public:
string name="张三";
protected:
string car="拖拉机";
private:
long card = 12345;
};
int main()
{
Person p;
p.name = "李四";//正确
p.car = "奔驰";//错误
p.card = 12346;//错误
system("pause");
return 0;
}
struct:默认权限是public
class:默认权限是private
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
class Person
{
public:
//名字可读可写
void set_name(string a)
{
name = a;
cout << "名字修改完毕!" << endl;
}
void get_name()
{
cout << "名字为:" << name << endl;
}
//车只读不写
void get_car()
{
cout << "车为:" << car << endl;
}
//银行卡密码只写不读
void set_card(long a)
{
card = a;
cout << "银行卡密码修改完毕!" << endl;
}
//所有属性设置为隐私权限,通过接口实现可读可写,可读,可写
private:
string name = "张三";
string car = "拖拉机";
long card = 12345;
};
int main()
{
Person p;
p.get_name();
p.set_name("李四");
p.get_car();
p.set_card(123);
system("pause");
return 0;
}
9、构造函数与析构函数
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数有编译器自动调用,无需手动调用。
析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
class Person
{
public:
//构造函数,初始化
/*
特点:1、没有返回值,不用写void
2、函数名与类名相同
3、构造函数可以有参数,可以发生函数重载
4、创建对象的时候,构造函数会自动调用,且只调用一次
*/
Person()
{
cout << "已调用构造函数" << endl;
}
//析构函数,清理
/*
特点:1、没有返回值,不用写void
2、函数名与类名相同,在函数名之前加一个~
3、析构函数不可以有参数,不可以发生函数重载
4、对象销毁前,会自动调用析构函数,且只调用一次
*/
~Person()
{
cout << "已调用析构函数" << endl;
}
};
void test1()
{
Person p1;
}
int main()
{
cout << "在main函数中实例化对象" << endl;
Person p2;//main函数中因system("pause"),所以程序不会结束,不会调用析构函数
cout << "在子函数中实例化对象" << endl;
test1();//子函数中实例化的对象在子函数调用结束之后会执行析构函数
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
class Person
{
public:
//构造函数的分类
/*
1、按照参数分类:①无参构造函数②有参构造函数
2、按照类型分类:①普通构造函数(无参和有参都是)②拷贝构造函数
*/
Person()
{
cout << "已调用无参构造函数" << endl;
}
Person(int a)
{
cout << "已调用有参构造函数" << endl;
}
//拷贝构造函数
Person(const Person &a)
{
cout << "已调用拷贝构造函数" << endl;
age = a.age;
}
~Person()
{
cout << "已调用析构函数" << endl;
}
int age;
};
void test1()
{
Person p1;
}
int main()
{
Person p1;//调用默认参数时不要加括号,否则编译器会把它当成一个函数申明处理
Person p2(10);
p2.age = 10;
//拷贝构造函数
Person p3(p2);
cout << "p3的age=" << p3.age << endl;
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
#define PI 3.14
class Person
{
public:
//构造函数的分类
/*
1、按照参数分类:①无参构造函数②有参构造函数
2、按照类型分类:①普通构造函数(无参和有参都是)②拷贝构造函数
*/
Person()
{
cout << "已调用无参构造函数" << endl;
}
Person(int a)
{
cout << "已调用有参构造函数" << endl;
}
//拷贝构造函数
Person(const Person &a)
{
cout << "已调用拷贝构造函数" << endl;
age = a.age;
}
~Person()
{
cout << "已调用析构函数" << endl;
}
int age;
};
void test1()
{
Person p1;
}
int main()
{
//1、括号法
Person p1;
Person p2(10);
Person p3(p2);//拷贝构造
//2、显示法
Person p4 = Person();
Person p5 = Person(10);
Person p6 = Person(p5);//拷贝构造
//3、隐式转换法
Person p7 = 10;//相当于Person p7=Person(10);
Person p8 = p7;//拷贝构造
Person(10);//匿名对象,在该行执行完之后对象立即被释放
system("pause");
return 0;
}
拷贝构造函数的使用的三种场景:
①使用一个已经创建完毕的对象来初始化一个新对象。
②值传递的方式给函数形参传值。
③以值方式返回局部对象。
默认情况下:C++编译器至少给一个类添加三个函数:
1、默认构造函数(无参,函数体为空)
2、默认析构函数(无参,函数体为空)
3、默认拷贝构造函数,对属性进行拷贝。
特点:①如果用户定义了有参构造函数,则C++不再提供默认无参构造,但会提供默认拷贝
②如果用户定义拷贝函数,C++不会再提供其他构造 函数。
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作。
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
Person()
{
cout << "已调用无参构造函数" << endl;
}
Person(int a)
{
age = a;
cout << "已调用有参构造函数" << endl;
}
~Person()
{
cout << "已调用析构函数" << endl;
}
int age;
};
void test1()
{
Person p1(16);
cout << "p1的年龄=" << p1.age << endl;
Person p2(p1);//没有写拷贝构造函数,编译器会自动给出,但是只是简单的赋值,是浅拷贝
cout << "p2的年龄=" << p2.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
Person()
{
cout << "已调用无参构造函数" << endl;
}
Person(int a,int b)
{
age = a;
Height = new int(b);
cout << "已调用有参构造函数" << endl;
}
~Person()
{
//清理堆区的数据
if (Height != NULL)
{
delete Height;
Height = NULL;
}
cout << "已调用析构函数" << endl;
}
int age;
//在成员属性中定义一个在堆区的数据
int *Height;
};
void test1()
{
Person p1(16,180);
cout << "p1的年龄=" << p1.age << endl;
Person p2(p1);//没有写拷贝构造函数,编译器会自动给出,但是只是简单的赋值,是浅拷贝
cout << "p2的年龄=" << p2.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
运行上面这个程序会引发异常,这是因为在 p1和p2都执行析构函数,所以将Height的地址释放了两次,本来只需要释放一次就行了。解决这个问题的方法就是自己写拷贝构造函数,对于在堆区定义的成员属性,在拷贝构造函数中再new一个空间。
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
Person()
{
cout << "已调用无参构造函数" << endl;
}
Person(int a,int b)
{
age = a;
Height = new int(b);
cout << "已调用有参构造函数" << endl;
}
Person(const Person& p)
{
age = p.age;
//Height = p.Height;编译器默认的浅拷贝
Height = new int(*p.Height);//深拷贝
}
~Person()
{
//清理堆区的数据
if (Height != NULL)
{
delete Height;
Height = NULL;
}
cout << "已调用析构函数" << endl;
}
int age;
//在成员属性中定义一个在堆区的数据
int *Height;
};
void test1()
{
Person p1(16,180);
cout << "p1的年龄=" << p1.age << endl;
Person p2(p1);//没有写拷贝构造函数,编译器会自动给出,但是只是简单的赋值,是浅拷贝
cout << "p2的年龄=" << p2.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
//类中类
class Phone
{
public:
Phone()
{
cout << "已调用Phone默认构造函数" << endl;
}
Phone(string a)
{
phone_name = a;
cout << "已调用Phone有参构造函数" << endl;
}
~Phone()
{
cout << "已调用Phone析构函数" << endl;
}
string phone_name;
};
class Person
{
public:
Person()
{
cout << "已调用Person无参构造函数" << endl;
}
Person(int a,string b)
{
age = a;
phone.phone_name = b;
cout << "已调用Person有参构造函数" << endl;
}
~Person()
{
cout << "已调用Person析构函数" << endl;
}
int age;
Phone phone;//这样定义一个Phone类,则在类Phone中要有默认构造函数,否则会出错
};
void test1()
{
Person p(10,"华为");
}
int main()
{
//在类中创建对象,先构造对象,再构造类;析构与之相反
test1();
system("pause");
return 0;
}
10、静态成员变量与函数
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
//静态成员函数,所有对象共享这个函数
static void func_1()
{
cout << "static void func_1()" << endl;
a = 20;
}
//静态成员变量,所有对象共享这个变量
static int a;
};
int Person::a = 10;//一定要有这一句
void test1()
{
Person p1;
Person p2;
p1.func_1();//p1的函数调用对p2起作用
//Person::func_1();//静态成员函数的另一种调用方式
cout << "p1.a=" << p1.a << endl;
cout << "p2.a=" << p2.a << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
class Person1
{
//空类只占一个字节,用于存放类的地址
};
class Person2
{
public:
int a;
};
class Person3
{
public:
int a;//非静态成员变量属于类上
static int b;//静态成员变量不属于类上
void function1()//非静态成员函数不属于类上
{
}
static void function2()//静态成员函数不属于类上
{
}
};
int Person3::b = 10;//一定要有这一句
void test1()
{
Person1 p1;
Person2 p2;
Person3 p3;
cout << "sizeof(p1)=" << sizeof(p1) << endl;
cout << "sizeof(p2)=" << sizeof(p2) << endl;
cout << "sizeof(p3)=" << sizeof(p3) << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
11、this指针
this指针的用途:
①当形参和成员变量名相同时,可以用this指针来区分
②在类的非静态成员函数中返回对象本身,可以用return *this
#include "iostream"
#include "string"
using namespace std;
class Person1
{
public:
void fun(int age)
{
age = age;//这个函数中三个age都是同一个东西
}
int age;
};
class Person2
{
public:
//this指针指向被调用的成员函数的所属的对象
void fun(int age)
{
this->age = age;
}
int age;
};
void test1()
{
Person1 p1;
Person2 p2;
p1.fun(10);
p2.fun(10);
cout << "p1.age=" << p1.age << endl;
cout << "p2.age" << p2.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
Person& fun(Person p)//返回类型加引用,否则每次返回都会创建一个新的对象
{
age += p.age;//将形参这个对象的年龄加到调用这个函数的对象的age上
return *this;
}
int age=10;
};
void test1()
{
Person p1;
Person p2;
p2.fun(p1).fun(p1);
cout << "p1.age=" << p1.age << endl;
cout << "p2.age=" << p2.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
12、常函数
常函数:用const修饰,常函数内不可以修改成员属性,但是mutable修饰的成员变量可以修改
常对象:用const修饰,常对象只能调用常函数。
#include "iostream"
#include "string"
using namespace std;
class Person
{
public:
//常函数,this的本质是Person * const this,加了const相当于const Person * const this,
//所以加了const后缀后连值也不能修改
void fun() const
{
//age = 20;//错误
b = 20;//正确
}
void fun_2()
{
}
int age;
mutable int b;
};
void test1()
{
Person p1;
const Person p2;
p2.fun();//正确
//p2.fun_2();//错误,常对象只能调用常函数
}
int main()
{
test1();
system("pause");
return 0;
}
13、友元
#include "iostream"
#include "string"
using namespace std;
//全局函数做友元
class Person
{
friend void test1();//加这一句则在test1()中可以访问类的private成员
public:
int get_age()
{
return age;
}
private:
int age = 10;
};
void test1()
{
Person p;
cout << p.age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
//类做友元
class Buliding
{
friend class Goodgay;
public:
Buliding();
string sittingroom;//客厅
private:
string bedroom;//卧室
};
Buliding::Buliding()
{
sittingroom = "客厅";
bedroom = "卧室";
}
class Goodgay
{
public:
Goodgay();
void visit();
Buliding *home;
};
//类外写成员函数
Goodgay::Goodgay()
{
home = new Buliding;
}
void Goodgay::visit()
{
cout << "好基友正在访问:" << home->sittingroom << endl;
cout << "好基友正在访问:" << home->bedroom << endl;
}
void test1()
{
Goodgay gg;
gg.visit();
}
int main()
{
test1();
system("pause");
return 0;
}
#include "iostream"
#include "string"
using namespace std;
//类做友元
class Buliding;
class Goodgay
{
public:
Goodgay();
void visit1();
void visit2();
Buliding *home;
};
class Buliding
{
friend void Goodgay::visit1();
public:
Buliding();
string sittingroom;//客厅
private:
string bedroom;//卧室
};
Buliding::Buliding()
{
sittingroom = "客厅";
bedroom = "卧室";
}
//类外写成员函数
Goodgay::Goodgay()
{
home = new Buliding;
}
void Goodgay::visit1()
{
cout << "好基友1正在访问:" << home->sittingroom << endl;
cout << "好基友1正在访问:" << home->bedroom << endl;
}
void Goodgay::visit2()
{
cout << "好基友2正在访问:" << home->sittingroom << endl;
//cout << "好基友2正在访问:" << home->bedroom << endl;//错误
}
void test1()
{
Goodgay gg;
gg.visit1();
gg.visit2();
}
int main()
{
test1();
system("pause");
return 0;
}
14、运算符重载
#include "iostream"
#include "string"
using namespace std;
//重载+,使两个类中对应变量相加
class Person
{
public:
int a=10;
int b=10;
//通过成员函数实现重载
Person operator + (Person &p)
{
Person temp;
temp.a = this->a + p.a;
temp.b = this->b + p.b;
return temp;
}
};
通过全局函数实现运算符重载
//Person operator+(Person &p1, Person &p2)
//{
// Person p;
// p.a = p1.a + p2.a;
// p.b = p1.b + p2.b;
// return p;
//}
void test1()
{
Person p1;
Person p2;
Person p3;
//本质p3 = p1.operator+(p2);
p3 = p1 + p2;
cout << "p3.a=" << p3.a << endl;
cout << "p3.b=" << p3.b << endl;
本质p3 = operator+(p1, p2);
//p3 = p1 + p2;
//cout << "p3.a=" << p3.a << endl;
//cout << "p3.b=" << p3.b << endl;
}
int main()
{
test1();
system("pause");
return 0;
}