类和对象是 C++的重要特性:
类(class)是一种构造类型,对象(object)类定义出来的变量,类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数。
- struct 只能包含变量
- struct 不能包含函数
- 因为C语言没有面向对象,所以struct没有(继承和多态)
- 访问权限只能是public
为了兼容C,C++也有struct,但C++中的结构体可以继承,可以多态,可以拥有函数
class比较全能:
- 可以拥有变量,函数
- 可以继承
- 可以发生多态
- struct 的成员默认为 public,class 的成员默认为 private
struct不可以用于定义模板参数,class 可以用于定义模板参数
struct 继承默认是 public 继承 ,class 继承默认是 private 继承
格式:
class 类名
{
权限:成员变量;成员函数;...
};
class person
{
public://权限
int a;//成员变量
void show()
{
cout << a << endl;//成员函数
}
protected:
double b;
private:
char c;
};
- class 类名 标识符
- 类名 标识符(建议使用)
class person
{
public:
int a;
void show()
{
cout << a << endl;
}
protected:
double b;
private:
char c;
};
int main() {
class person p1;//方式一
person p2; //方式二
return 0;
}
- 成员变量
- 成员函数
- 成员函数在类内声明和定义的话默认为内联函数
- 成员函数在类内声明如果在类外定义的话,不为内联函数,需要在定义处添加inline
当类中的成员函数较少时,一般使用类内定义
当类中的成员函数较多时,为了不影响类中结构的观察,一般会使用类内声明,类外实现
类外定义格式:
(inline)返回类型 类名:: 函数名(形参,...)
- 不为内联函数的话,不加inline
- 类名:: 的作用是 限定为 该类中的函数
class person
{
public:
int a;
int text1()//类内声明和定义
{
return a;
}
int text2();//类内声明
};
inline int person::text2()//类外定义
{
return a + 1;
}
public : 共有权限,类内外都可以访问
- protected:保护权限 ,仅类内可以访问
- private: 私有权限,仅类内可以访问
protected和private的区别:在继承时会仔细分析。
class person
{
public:
int a;
int text1() //类内可以访问全部成员
{
return a;
}
protected:
double b;
double text2()
{
return b;
}
private:
char c;
char text3()
{
return c;
}
};
int main() {
person p2;
p2.a = 10;//公有类型 类外可以访问
p2.b = 10;//(报错)保护类型 类外不可以访问
p2.c= 10; //(报错)私有类型 类外不可以访问
return 0;
}
封装,是指尽量隐藏类的内部实现,只向用户提供有用的成员函数。
为了保护类内的成员变量,防止被随意修改,一般会把成员变量设置为private类型,然后定义几个接口来访问私有数据,以达到修改私有属性。常见的接口为get() set()
class person
{
public:
int a;
void show()
{
cout << a << endl;
}
};
int main() {
person p;
p.a = 10;//可直接修改,不安全
return 0;
}
class person
{
public:
void show()
{
cout << a << endl;
}
//获取a的值
int get_a()
{
return a;
}
//设置a的值
void set_a(int p)
{
a = p;
}
private:
int a;
};
int main() {
person p;
p.a = 10;//错误,私有成员不可直接访问
p.set_a(20);//设置a的值 (更安全)
p.get_a();//得到a的值
return 0;
}
- 普通对象用 . 访问类中的成员和函数
- 指针的话用 -> 访问类中的成员和函数
person p2;
p2.a = 10;
person *p = new person;//开辟内存
p->a = 10;
delete p;//释放内存
构造函数:是一种特殊的成员函数
作用:初始化对象的数据成员
构造函数的特点:
格式: 类名(形参){ 操作;}
class person
{
public:
person()//构造函数没有返回值,函数名与类名相同
{
cout << "构造函数" << endl;
}
person(int a,double b)//构造函数可以发生重载(作用为初始化数据)
{
_a = a;
_b = b;
cout << "构造函数2" << endl;
}
int _a;
double _b;
};
int main() {
person p;//实例化时自动调用构造函数
person p1(1, 2.0);//实例化时自动调用构造函数
return 0;
}
例如:构造函数为:person(int a,int b){}
指针对象:
class person
{
public:
person(int a, int b)
{
cout << "构造函数调用" << endl;
}
int _a;
double _b;
};
int main() {
person p(10, 20);
person p1=person(10, 20);
person p2{ 10, 20 };
person p3 = { 10, 20 };
person * pp1 = new person(10, 20);
person * pp2 = new person{ 10,20 };
delect(pp1);
delect(pp2);
return 0;
}
构造函数的易错点:
构造函数易错点_旷工锁的博客-CSDN博客
- 只有默认构造函数”被需要“的时候编译器才会生成默认构造函数
- 默认构造函数没有形参,不对数据做任何处理。
- 一旦定义了构造函数,那么不会生成默认构造函数。
此块内容来自:深度探索C++对象模型(侯捷)(推荐大家使用这本书)
1.当一个类内含另一个类对象时且该类对象有默认构造
class person
{
public:
person()//默认构造
{
}
int _a;
double _b;
};
class son
{
public:
person p;
int s;
};
int main()
{
son k();//这是son会生成默认构造函数
return 0;
}
class person
{
public:
person()
{
}
int _a;
double _b;
};
class son :public person//son 继承于person
{
public:
person p;
int s;
};
int main()
{
son k;//这是son会生成默认构造函数
return 0;
}
class person
{
public:
person(int a,double b)
{
_a = a;
_b = b;
}
int _a;
double _b;
};
格式 :类名 (类型 标识符,...) : 成员变量(标识符),成员变量(标识符)...{ }
这种方法更加的简便
person(int a,double b):_a(a),_b(b)
{
}
int _a;
double _b;
类初始化的顺序为 成员变量声明的顺序,先初始化成员,再初始化函数
展示一个案例:(易发生的错误)
class person
{
public:
person(int a):_a(_b),_b(a),_c(_b)
{
}
int _a;
int _b;
int _c;
};
int main()
{
person p(4);
cout << p._a << endl;//结果为-858993460
cout << p._b << endl;//结果为4
cout << p._c << endl;//结果为4
return 0;
}
class person
{
public:
person()
{
cout << "构造函数" << endl;
}
~person()//析构函数,没有返回值,没有参数,没有重载,不能显示调用,
{
cout << "析构函数" << endl;
}
};
int main()
{
person p;
return 0;
}
class person
{
public:
person()
{
cout << "构造函数" << endl;
}
~person()
{
cout << "析构函数" << endl;
}
};
int main()
{
person p; //调用了 构造和析构
return 0;
}
class person
{
public:
person()
{
cout << "构造函数" << endl;
}
~person()//析构函数,没有返回值,没有参数,没有重载,不能显示调用,
{
cout << "析构函数" << endl;
}
};
int main()
{
person *p;//不会调用构造和析构
cout<<"----------"<
下面给出例题:
class person
{
public:
person(){}//默认构造
person(const person&p)//浅拷贝构造函数
{
_age = p._age;
_height = p._height;
}
int _age;
double _height;
};
int main()
{
person p;
person p1(p);//浅使用拷贝函数
return 0;
}
class person {
public:
//初始化
person(int a, string n)
{
cout << "有参构造函数的调用" << endl;
age = a;
name = new string(n);
}
~person()//析构函数
{
//释放内存
if (name != NULL)
{
delete name;
name = NULL;
}
cout << "析构函数的调用" << endl;
}
//自定义拷贝构造
person(const person &p)
{
age = p.age;
//在堆区申请一块内存 指向不一样的内存
name = new string(*p.name);
}
int age;
string *name;
};
int main()
{
person p1(15, "name");
//深拷贝
person p2 = p1;
return 0;
}
- 浅层复制 :只复制指向对象的指针,而不复制引用对象本身
深层复制 :复制引用对象本身
正常情况下:创建一个类时编译器会自动生成3个函数
1.默认构造函数
2.析构函数
3.拷贝构造函数(浅拷贝)
当用户定义有参构造函数,则c++不会提供默认构造函数 但还会提供拷贝构造函数(值拷贝)
当定义拷贝构造时,c++不会提供其他构造函数
class person
{
public:
person(const person &p)//拷贝构造函数
{
_name = p._name;
_age = p._age;
_height = p._height;
}
string _name;
int _age;
int _height;
};
int main()
{
person p;//报错,没有默认构造函数
return 0;
}
class person{};
class son{
person p;
};
class person;//前向引用声明
class son{
person p;
}
class person{}
注意:在提供一个完整的类定义之前,不能定义该类对象,也不能在内联成员函数中使用该类的对象
class person;
class son
{
public:
person p;//报错son::p使用未定义的 class“person”
};
class person
{
public:
};
int main()
{
son s1;
return 0;
}
- 按照初始化顺序调用构造函数
- 如果对象成员有构造函数,那么如果类没有构造函数,会生成默认构造函数
- 初始对象成员时,需要按照对象成员的构造函数来实现
#include
#include
using namespace std;
class person
{
public:
//构造函数
person();
person(string na, int a, int h) :name(na), age(a), height(h)
{
cout << "person的构造函数" << endl;
}
~person()
{
cout << "person的析构" << endl;
}
string name;
int age;
int height;
};
class son
{
public:
son(string s, string s1, int a, int h) :Name(s), P(s1, a, h)
{
cout << "son的构造" << endl;
}
~son()
{
cout << "son的析构" << endl;
}
string Name;
person P;
};
void text()
{
son S1("A", "B", 12, 160);//初始化
cout << "父亲的名字"<
- this时c++的一个关键词,是一个const指针,可以用this指向对象中的成员
- this指针是隐含每一个非静态成员函数的指针
- this不需要定义,直接使用
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中。
class person
{
public:
person(string name, int age)
{
//使用this指针区分形参和成员变量
this->name = name;
this->age = age;
//不使用也可以 但代码可读性差
//name = name;
//age = age;
}
string name;
int age;
};
class person
{
public:
person get_this()
{
//返回本身
return *this;
}
};
class person
{
public:
void show_age()
{
//可以做一个判断,防止出现错误
if (this == NULL)
{
return;
}
cout << "age=:" << age << endl;
//等价与cout<<"age=:" << this->age << endl;
//因为this为空指针,所以报错
}
int age;
};
int main()
{
//此时指针为空指针
person *p = NULL;
}
小结参考自:在类的成员函数中调用delete this - 向日葵的祈愿 - 博客园 (cnblogs.com)
this指针是可以被delete的:原理为this是一个指针,使用delete会调用析构函数,然后再释放内存。
- delete this 不能放在析构函数中,会造成死循环,会反复调用析构函数
- delete this放在非析构函数中(创建普通对象时),delete this 之后 不能访问类中的成员(因为使用类成员,靠的是this指针)
- delete this放在非析构函数中(创建指针对象时),delete this 之后 不能访问类中的成员
注意:delete 只会释放内存,并不会把指针赋予NULL
以下测试在vs2015中,不同的编译器可能会有差异
//析构函数中调用delete this
class person
{
public:
person(){}
~person() {
cout << "delete this" << endl;//结果持续输出这条语句
delete this;
}
int a = 0;
};
int main()
{
person p;
return 0;
}
//delete this 放在普通成员中,创建普通对象
class person
{
public:
person(){}
~person() {
}
void show()
{
cout << a << endl;
delete this;//在这里delete this 之后不能访问类的成员函数和变量
this->a = 10;//这条语句错误
a=10;//错误 //本本质上也是调用this指针访问
text();//错误
cout << a << endl;
}
void text()
{
cout << "show" << endl;
}
int a = 1;
};
int main()
{
person p;//创建普通对象
p.show();
p.a=10;//错误,无法访问
return 0;
}
//delete this 放在普通成员中,创建指针对象
class person {
public:
person() {
member = 1;
}
~person() {
member = 0;
}
void test() {
delete this;
}
void show()
{
cout << "show" << endl;
}
private:
int member;
};
int main() {
person *p = new person();//创建指针对象
p->test();
p->show();//运行报错
delete p;
return 0;
}
静态成员变量:在成员变量之前加上 static
注意:静态成员函数已经在类外初始化,无法用构造函数初始化
类内声明: static 数据类型 标识符;
类外实现: 数据类型 类名::标识符=初始化数据;
class person
{
public:
//静态成员变量
static int age;
};
//类外初始化
int person::age = 0;
class person
{
public:
person(){}
string _name;
static int _age;//类内声明
};
int person:: _age=10;//类外初始化
int main()
{
person p;
p._age = 20;//通过对象访问
person::_age = 30;//通过类名访问
return 0;
}
注意:static 成员变量的内存,而是在(类外)初始化时分配。反过来说,没有在类外初始化的 static 成员变量不能使用
class person
{
public:
person(){}
string _name;
static int _age;//类内声明
};
int person:: _age=10;//初始化时分配内存
int main()
{
person p;
return 0;
}
static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问
class p
{
public:
static int a;
};
int p::a = 10;
int main()
{
p::a = 20;//不用生成对象也可以访问
}
静态成员函数:在成员函数之前加上 static
静态成员函数只能访问静态成员变量
class person
{
public:
person(){}
person(string name):_name(name){}
static void show()
{
cout << _name << endl;//报错,无法访问非静态成员变量
cout << _age << endl; // 可以访问
}
string _name;
static int _age;
};
总结:静态成员和非静态成员的区别:
非静态成员: | 静态成员: |
只有本对象能使用 | 所有对象共用一个成员 |
类内声明用构造函数初始化 | 类内声明,类外初始化 |
构造函数初始化后,分配内存 | 类外初始化时分配内存 |
创建对象才能访问 | 不用创建对象也能访问(受权限限制) |
可以使用this指针 | 不能有this指针(公有的) |
const修饰的成员变量:
- 仅有const修饰的成员数据,类内声明,只能通过初始化来赋值(构造函数)
- static const修饰的成员数据,(整型和枚举类型)可以在类内初始化,其他的按照const初始化
注意:常成员数据的值不能修改
class person
{
public:
person(int b) :a(b){}//常成员的初始化
~person(){}
const int a;
};
class person
{
public:
person(){}
~person(){}
const int a=20;//常成员的初始化
};
错误的初始化:
class person
{
public:
person(int b)
{
a = b;//因为a为常量不能被赋值
}
~person(){}
const int a;
};
静态常成员数据(整形和枚举类型可以在类内初始化):
整形数据和枚举类型,在类内初始化:
class person
{
public:
static const int a=10;//可在类内初始化
static const enum S1 { A, B = 8, C, D, E };//可在类内初始化
};
其他类型则不能在类内初始化,只能在类外初始化:
class person
{
public:
static const float a;
static const double b;
static const string c;
};
//非整形和枚举类型只能在类外初始化
const float person::a = 10.0;
const double person::b = 12.00;
const string person::c = "pppppp";
- 常成员函数,是用const修饰的成员函数(定义时也要加const)
- 常成员函数,修改不了成员数据
- 常成员函数可以区分重载
常成员函数的定义:
class person
{
public:
void show()
{
cout << "show" << endl;
}
void show()const //const符号加到()后面 ,常成员函数可以区分重载
{
cout << "const show" << endl;
}
};
常成员函数不能修改成员数据:
class person
{
public:
void show()
{
a = 20;//可以,普通成员函数可以修改成员变量
cout << "show" << endl;
}
void show()const
{
a = 20;//报错,常成员函数无法修改成员变量
cout << "const show" << endl;
}
int a=10;
};
- 定义时用const修饰
- 常对象初始化后,不能更新数据
- 常对象只能调用常成员函数,不能调用其他成员函数
常对象的定义:
using namespace std;
class person
{
public:
person(int a1,string b1):a(a1),b(b1){}
int a;
string b;
};
int main() {
const person p1(10,"pppppp");//定义时用const修饰
p1.a = 20;//报错,常对象无法修改成员变量
return 0;
}
常对象只能调用常成员函数:
class person
{
public:
person(int a1,string b1):a(a1),b(b1){}
int a;
string b;
void show()
{
cout << "show" << endl;
}
void show()const
{
cout << "const show" << endl;
}
};
int main() {
const person p1(10,"pppppp");//定义时用const修饰
p1.show();//常成员调用常成员函数
return 0;
}
常引用的作用是防止实参传到形参是数据被修改
class person
{
public:
//默认构造函数
person();
//拷贝构造函数
person(const person &b)
{
//加了const可以防止 b中的数据被修改
this->b = b.b;
a = new int(*b.a);
}
int *a;
int b;
};
class person
{
public:
person(int a1):a(a1){}
void show()const
{
a = 20; //可以,mutable使 a可以在常成员函数中修改
}
mutable int a;
};
int main() {
const person p1(10);
p1.a = 30;//常对象中也能修改
return 0;
}
- 在类中,常函数没有重载时,会调用常函数
- 在类中,如果有重载时,会优先调用非常函数
class person
{
public:
void show()const
{
cout << "const show" << endl;
}
};
int main() {
person p1;
p1.show();//调用show函数,调用的是const类型的
return 0;
}
class person
{
public:
void show()
{
cout << "show" << endl;
}
void show()const
{
cout << "const show" << endl;
}
};
int main() {
person p1;
p1.show();//调用show函数,优先调用非const类型的
return 0;
}
调用:常函数在调用时,不能使用const修饰
class person
{
public:
void show()
{
cout << "show" << endl;
}
void show()const
{
cout << "const show" << endl;
}
};
int main() {
person p1;
p1.show()const;//错误,不能这么使用
p1.const show()//错误,不能这么使用
return 0;
}
- 空类所占的内存为1,用来区分对象
- 函数不存储在类中
- 静态成员不存储在类中
- 非静态成员数据存储在类中(常量和变量)
- 指针 4个字节(32位),8个字节(64位)
注意:在类中也按照字节对齐的方式来计算内存,内存不受数据权限的印象(仅在单个类中,非继承)
class person
{
};
int main()
{
person p;
cout << sizeof(p) << endl;
return 0;
}
class person
{
public:
person(){}
~person(){}
void show()
{
cout << "show" << endl;
}
};
int main()
{
person p;
cout << sizeof(p) << endl;
return 0;
}
class person
{
public:
static int a;
static const int b = 20;
};
int person::a = 10;
int main()
{
person p;
cout << sizeof(p) << endl;
return 0;
}
class person
{
public:
char c;
int a;
const int b = 20;
};
int main()
{
person p;
cout << sizeof(p) << endl;
return 0;
}
class person
{
public:
person(){}
void show() { cout << "show" << endl; }
void show() const{ cout << "show" << endl; }
char c;
int a;
const int b = 20;
static int b1;
static const int b2 = 20;
int *p;//指针占4个字节(32位),(64位)8个字节
};
int person::b1 = 20;
int main()
{
person p;
cout << sizeof(p) << endl;
return 0;
}