如是初学者,建议按照以下顺序学习
1、C++入门基础
学习本章 需要掌握C语言
中的结构体
对于C
语言来说是 面向过程的语言
C++
是面向对象+面向过程,因为C++
兼容C
java
是面向对象的语言。
定义
举例:今晚吃 西红柿鸡蛋盖饭
面向过程:需要自己做,1.切西红柿备用。2.起锅烧油。3.放入鸡蛋炒熟备用…
面向对象:点了个外卖(西红柿炒鸡蛋盖饭)
先看代码 ,后面会解释
class Person //类定义的对象
{
public://访问限定符
//成员函数
void PrintPersonInfo();
private:
//成员变量
char _name[20];
char _gender[3];
int _age;
};
在C中有 struct 定义的结构体
在C++ 中兼容C 的结果构体,但是已经升级为类
并且默认访问 是public(公有)
而且在结构体中可以定义成员函数 并且调用
struct Stucent
{
//变量
char name[10];
int age;
int id;
};
int main()
{
struct Stucent s1; //兼容C
strcpy(s1._name, "李四");
s1.id = 1;
s1.age = 18;
printf("%s\n%d\n%d\n",s1.name,s1.age,s1.id);
}
在C++中用class 定义类
class Stucent
{
public:
//成员变量
char _name[10];
int _age;
int _id;
//成员函数
void Init(const char* name, int age, int id)
{
strcpy(_name, name);
_age = age;
_id = id;
}
private: //以下是私有
void Print()
{
cout << _name << endl;
cout << _age << endl;
cout << _id << endl;
}
};
int main()
{
Stucent s2; //升级到类,Stucent类名,也是类型
strcpy(s2._name, "李四");
s2._id = 1;
s2._age = 18;
s2.Init("张三",18, 1);
s2.Print();
}
在C++
中用class
定义类,类中包含 成员变量、成员函数;
class
中定义的类 是有访问权限的,默认是私有的,外部函数不可以访问
面向对象的三大特性:封装、继承、多态。
默认
访问限定符是private
(私有)
访问限定符:
public
公有
protected
保护
private
私有
访问限定符的作用域
访问限定符的作用域 遇到下一个限定符结束 或者类的底部结束
类的实例化
用类名定义的 变量 - 称之为 类实例化的对象
C++类中的成员对象 也就是C结构体中的变量
类对象的大小
类中有两种对象 - 成员变量 、成员函数
存储方式 是 实例化对象字存储成员变量、成员函数放在公共代码区,因为 成员函数 是相同的 没必要多处拷贝
// 类中既有成员变量,又有成员函数
class A1 {
public:
void f1(){}
private:
int _a;
};
// 类中仅有成员函数
class A2 {
public:
void f2() {}
};
// 类中什么都没有---空类
class A3
{};
sizeof(A1) : __4___ sizeof(A2) : __1___ sizeof(A3) : ___1__
类的大小也需要空间对齐,规则与结构体相同
类无变量时 用一个字符标识这个类,存了一个地址
六个默认成员函数:构造函数、析构函数、拷贝构造函数、赋值运算符重载
在实例化对象时初始化用的函数,我们自己写了,编译器会调用我们写的,我们不写,编译器会自动生成。对于内置类型成员变量不处理,对于自定义类型成员变量调用它的默认构造函数。
class Date
{
构造函数 要与定义的类名一致
1.Date() {}; 不能传参
2.Date(int a,int b){};必须传参
3.Date(int a = 0,int b = 0){}; 不传参用缺省值,传参用形参
4.Date(capacaty)//对于栈的初始化
{
Stack* ret = (Stack*)malloc(sizeof(Stack)*capacaty)
_capacaty = capacaty;
}
};
用于清理对象中的资源,如果独享需要资源,才需要自己实现析构函数,析构函数在函数生命周期到了,自动调用。内置类型不处理,自定义类型调用它的析构函数。
1.对于malloc的对象需要自己定义析构函数
class Stack
{
int*a;
int _val;
};
2.对于日期类对象不需要自己定义析构函数
class Date
{
int _year;
int _month;
};
使用同类型的对象去初始化对象,我们不写,编译器自动生成。
自动生成
1.内置类型完成按字节序的拷贝 - 浅拷贝 对于指针的拷贝会把指针的地址拷贝过去,造成新初始化的对象与原对象指向同一块空间。
2.自定义类型的成员变量,调用它的拷贝构造。
1.对于日期类,可以不写,浅拷贝够用
Date(const Date& d) 需要注意这里传引用,不然会死循环调用拷贝构造
{
_year = d._year;
_month = d._month;
_day = d._day;
}
2.对于栈类,需要拷贝构造,因为有指针
3.对于以下,不需要,因为会调用它的拷贝构造
class MyQueue
{
Stack _pushST;
Stack _popST;
}
关键字:返回值类型operator
操作符(参数列表)
自定义类型不支持运算符使用,所以需要自己写
不能被重载的运算符(5个):.*
、::
、sizeof
、?:
、.
大于操作符,用引用是为了防止原始数据被改,并可以减少拷贝构造的调用
class Date
{
public:
bool Date::operator>(const Date& d)
{
if(_year>d._year)
return true;
else if(_year==d._year && _month>d._month)
return true;
else if(_year==d._year && _month==d._month && _day>d._day)
return true;
else
return false;
}
private:
int _year;
int _month;
int _day;
}
调用:
int mian()
{
d1 > d2; //d1.operator>(d2)
}
说明:、
如果operator 与main在一个.cpp中 则可以 operator>(d1,d2);这样调用
如果:1.operator>在类中,则字默认增加隐藏参数 this
2.所以要减少一个参数,this 就是d1
运算符重载 调用顺序,先去成员函数找,再去全局找
两个已经存在的对象之间 赋值
Date d1(2022,4,17);
Date d3(2022,5,23);
对刚创建的值进行初始化 - 拷贝构造
Date d4(d1);
Date d5 = d1;
两个已经存在的对象之间 赋值 - 赋值拷贝
d1 = d3;
赋值运算符的返回值应该是d1
int i,j,k;
j = i = k = 10;
//k = 10的返回值是k
赋值运算符的实现
Date& operator=(const Date& d)
{
优化:自己个自己赋值 判断一下
if(this != &d)
{
_year = d.year;
_month = d.month;
_day = d.day;
}
return *this;
}
因为 函数调用结束,d1还存在,所以用引用返回,可以减少拷贝构造
赋值重载、拷贝构造、构造函数、析构函数分别是干嘛用的?
1.赋值重载
:给两个已经存在的对象进行复制,在这期间会产生临时拷贝,所以会调用拷贝构造,为了减少拷贝构造,参数用了引用参数,因为赋值运算要有返回值作为右值,所以需要返回this参数,,为了减少拷贝构造,返回参数用了引用返回。
2.拷贝构造
:用来对刚创建的对象进行赋值,也用于函数传参,临时拷贝用
3.构造函数
:用于初始化新建的类,一般会用缺省参数,便于创建对象时的赋值。
4.析构函数
:对对象的清理
赋值重载和拷贝构造 代码基本一致
构造函数和析构函数 代码基本一致
以上函数都是 编译器自动调用,不写时自动产生。
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
Date Date::operator+(int day)
{
Date ret = *this;
ret += day;
return ret;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date ret = (*this);
*this + 1;
return ret;
}
**日期类 请单独查看**
析构 在同一个生命周期里 先创建的后析构
const 修饰指针
const 放* 左面 修饰*p不能改
const 放* 右面 修饰p 不能改
void Display() const
{
}
void Display(const Date* this)
{
}
用来修饰 this指针 意思是指针指向的内容不能被修改
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ; // 年
int _month ; // 月
int _day ; // 日
};
class B
{
public:
B(int a, int ref)
:_aobj(a)
,_ref(ref)
,_n(10)
{}
private:
A _aobj; // 没有默认构造函数
int& _ref; // 引用
const int _n; // const
};
对于引用成员、const成员、自定义类型成员,需要在初始化列表初始化
static成员 初始化,静态变量一定在类外初始化
使用初始化列表可以更高效
Date(const A& aa)
:_aa(aa)
{}
int main()
{
A aa(10);
}
上述调用一次拷贝构造 和一次构造函数
Date(const A& aa)
{
:_aa(aa)
}
int main()
{
A aa(10);
}
上述调用一次拷贝构造 和两次构造函数
因为初始化列表是成员变量定义的地方,所以_aa会在初始化列表定义的调用默认构造函数初始化
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)
class Sum
{
public:
Sum()
{
_ret += _i;
++_i;
}
static int GetRet()
{
return _ret;
}
private:
static int _i;
static int _ret;
};
int Sum::_i = 1;
int Sum::_ret = 0;
class Solution {
public:
int Sum_Solution(int n) {
//Sum arr[n-1];
//return Sum().GetRet();
Sum arr[n];
return Sum::GetRet();
}
};
结构体数组 定义的时候n是多少就会调用多少次默认构造函数
private:
// 非静态成员变量,可以在成员声明时给缺省值。
private:
int _a1 = 0; //C++ 11 补丁,给成员变量缺省值
B _bb;
B _bb2 = 10;
B _bb3 = B(10);
int* p = (int*)malloc(4 * 10);
//vs2019 支持
int arr[10] = { 1,2,3,4,5,6,7 };
// 构造函数 不给静态变量初始化,静态不能这样给缺省值,
// 必须在类外面全局定义初始化
//static int _sCount = 0;
};
这里不是初始化 是缺省值
传参用传参
构造函数有缺省值用构造函数缺省值
最后用成员声明处的缺省值
关键字friend
外部函数 在类内使用友元函数则可以访问类内的私有成员变量
friend ostream& operator<<(ostream& _cout, const Date& d);
Date可以访问Time类
class Time
{
friend class Date;
}
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static、枚举成员,不需要外部类的对象/类名。
3. sizeof(外部类)=外部类,和内部类没有任何关系。
A不能访问B成员 B能访问A
class A
{
private:
static int k;
int h;
public:
class B
{
public:
void foo(const A& a)
{
cout << k << endl;//OK
cout << a.h << endl;//OK
}
};
};
int A::k = 1;
int main()
{
A::B b;
b.foo(A());
return 0;
}
类和对象完