C++学习笔记——类和对象-创新互联
- 前言
- 一、类与对象的概念
- 二、封装
- 1. 封装的作用
- 2. 代码示例
- 三、类的初始化和清理
- 1. 构造函数&析构函数
- 2. 拷贝构造函数
- 3. 深拷贝&浅拷贝
- 4.初始化列表
- 5.类对象作为类成员
- 6.静态成员
- 四、类成员储存方式
- 五、this指针
- 六、const修饰成员函数
前言
类和对象的学习主要包括一下几点:
- 类与对象的概念
- 封装
- 类的初始化和清理
- 类的储存
- this指针
- 友元
- 运算符重载
- 继承
- 多态
接下来我将对以上内容进行一一讲解。
一、类与对象的概念
类:用来对实体(对象)进行描述,对象的属性,以及对象的行为。
对象:对象可以是一个实体、一个名词、一个可以想象为有自己标识的任何东西,所有东西都能当做对象,Everything is object(万物皆对象)。
C++面向对象的三大特性为:封装、继承、多态。
二、封装 1. 封装的作用- 将属性和行为作为一个整体,然后通过对象来完成操作。
- 使属性和行为附加权限
- 尽量使代码或者功能独立
求圆的周长
#includeusing namespace std;
#define PI = 3.14; //宏定义π的值
//封装一个圆类,求圆的周长
//class代表设计一个类,后面跟着的是类名
class Circle
{public: //访问权限为公共的
//行为:
//获取到圆的周长
double calculateZC()
{return 2 * PI * m_r;//返回计算的圆的周长
}
void func()
{m_r = 10;
}
private:
//属性:
int m_r;//半径
};
int main()
{Circle c1;//创建一个圆的对象
//c1.m_r = 10; //m_r为私有属性,无法进行赋值操作
c1.func();//调用幅值的函数
cout<< "圆的周长为: "<< c1.calculateZC()<< endl;//打印
system("pause");
return 0;
}
三、类的初始化和清理
1. 构造函数&析构函数构造函数:
- 用于创建对象时为对象的成员属性赋值
- 没有返回值也不写void
- 函数名和类名相同
- 格式:类名() { }
- 可以重载
- 当实例对象的时候,无需手动调用,会自动调用,只调用一次
- 编译器会提供默认的构造函数
- 定义了其他的构造函数,编译器不会提供默认构造函数
- 分类:无参构造函数,有参构造函数,拷贝构造函数
构造函数的调用方式:
- 括号法
- 显示法
- 隐式转换法
析构函数:
- 用于对象销毁前系统自动调用,执行一些清理工作
- 没有返回值也不写void
- 格式:~类名() { }
- 可以重载
- 对象销毁时会自动调用,无需手动调用,只调用一次
代码示例:
#includeusing namespace std;
class Person
{public:
//无参构造函数
Person()
{cout<< "Person的构造函数调用"<< endl;
}
//有参构造函数
Person(int a)
{m_A = a;
cout<< "Person的有参构造函数!"<< endl;
}
//拷贝构造函数
Person(const Person &p)
{m_A = p.m_A;
cout<< "Person的拷贝构造函数!"<< endl;
}
//析构函数
~Person()
{cout<< "Person的析构函数调用"<< endl;
}
private:
int m_A;
};
int main()
{Person p1;//无参构造函数的调用,调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
//有参构造函数的调用
Person p2(10);//括号法调用有参构造函数
Person p3 = Person(10);//显式法调用有参构造函数
Person p4 = Person(p3);//显式法调用拷贝构造函数
Person p5 = 10;//隐式转换法调用有参构造函数
Person p6 = p5;//隐式转换法调用拷贝构造函数
system("pause");
return 0;
}
2. 拷贝构造函数拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
在c++中拷贝构造函数通常在以下三种情况下调用
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
使用时需要注意:
- 如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
- 如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
- 如果用户提供拷贝构造,编译器不会提供其他构造函数
示例代码:
class Person
{public:
Person()
{cout<< "无参构造函数!"<< endl;
mAge = 0;
}
Person(int age)
{cout<< "有参构造函数!"<< endl;
mAge = age;
}
Person(const Person& p)
{cout<< "拷贝构造函数!"<< endl;
mAge = p.mAge;
}
~Person()
{cout<< "析构函数!"<< endl;
}
private:
int mAge;
};
//1. 使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{Person p1(100); //创建man对象
Person p2(p1)); //调用拷贝构造函数,做对象拷贝操作
}
//2. 值传递的方式给函数参数传值,相当于Person p1 = p;
//对象作为参数,实质是调用了拷贝构造函数
void doWork(Person p1) {}
void test02()
{Person p; //无参构造函数
doWork(p);
}
//3. 以值方式返回局部对象,实质是调用了拷贝构造函数
Person doWork2()
{Person p1;
cout<< (int *)&p1<< endl;
return p1;
}
void test03()
{Person p = doWork2();
cout<< (int *)&p<< endl;
}
3. 深拷贝&浅拷贝浅拷贝:简单的赋值拷贝操作,不重新申请资源
深拷贝:在堆区重新申请空间,进行拷贝操作
示例代码:
#includeusing namespace std;
class Person
{public:
Person()
{cout<< "无参构造函数调用"<< endl;
}
~Person()
{if (m_Height != NULL)
{ delete m_Height;
m_Height = NULL;
}
cout<< "析构函数的调用"<< endl;
}
Person(int age, int height)
{m_Age = age;
m_Height = new int (height);
cout<< "有参构造函数的调用"<< endl;
}
Person(const Person& p)
{m_Age = p.m_Age;
//m_Height = p.m_Height;
m_Height = new int(*p.m_Height);
cout<< "拷贝构造函数的调用"<< endl;
}
int m_Age;
int* m_Height;
};
void test02()
{Person p1(10,100);
Person p2(p1);
}
//如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
int main()
{test02();
system("pause");
return 0;
}
Person类中的m_Height成员是一个指针变量,如果将Person类中的拷贝构造函数关于m_Height 的复制操作写为:m_Height = p.m_Height; 此时该拷贝构造函数为系统默认的拷贝构造函数的形式。
代码编译没有错误,但是运行后会报错:
此报错的原因为:
系统提供的默认构造函数为浅拷贝操作,并没有为变量开辟新的内存空间,所以p1和p2两个对象的m_Height成员都指向了同一块地址,导致析构函数对同一块地址释放了两次。
所以必须将拷贝构造函数中的m_Height幅值操作写为m_Height = new int(*p.m_Height); 则可以开辟新的内存,避免了内存的重复释放。
C++提供了初始化列表语法,用来初始化属性。
语法:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括 号中的初始值或表达式。
构造函数():属性1(值1),属性2(值2)… {}
示例代码:
#includeusing namespace std;
class Person
{public:
Person(int a, int b) :m_R(a), age(b) {}
void aprint()
{cout<< m_R<< age<< endl;
}
private :
int m_R;
int age;
};
int main()
{Person p(10,100);
p.aprint();
system("pause");
return 0;
}
5.类对象作为类成员在C++中,类对象也可以作为一个类成员。
示例代码:
#includeusing namespace std;
class Phone
{public:
Phone(string phone) :m_PhoneName(phone)
{cout<< "Phone 的构造"<< endl;
}
~Phone()
{cout<< "Phone 的析构函数"<< endl;
}
string m_PhoneName;
};
class Person
{public:
Person(string name, string phone) :m_Name(name), m_Phone(phone)
{cout<< "Person 的构造"<< endl;
}
~Person()
{cout<< "Person 的析构函数"<< endl;
}
void playGame()
{cout<< m_Name<< " 使用"<< m_Phone.m_PhoneName<< " 牌手机!"<< endl;
}
string m_Name;
Phone m_Phone;
};
void test01()
{Person p = Person("张三", "iphone xR");
p.playGame();
}
int main()
{test01();
system("pause");
return 0;
}
6.静态成员由关键字static修饰的成员变量和成员函数,成为静态成员。
静态成员所有对象共享同一块内存,不管有多少对象,静态成员只有一份存于公共内存中。
静态成员变量在类内声明,类外初始化。
示例代码:
#includeusing namespace std;
class Person
{public:
static int m_B;
private:
static int m_A;
};
int Person::m_A = 100;//私有成员也需要在类外初始化
int Person::m_B = 200;
int main()
{Person p1;
Person p2;
//因为占用同一块内存,所以两个对象打印的m_B的值相等。
cout<< p1.m_B<< endl;
cout<< p2.m_B<< endl;
system("pause");
return 0;
}
静态成员函数,静态成员函数只能访问静态成员变量。
示例代码:
class Person
{public:
//静态成员函数特点:
//1 程序共享一个函数
//2 静态成员函数只能访问静态成员变量
static void func()
{cout<< "func调用"<< endl;
m_A = 100;
//m_B = 100; //错误,不可以访问非静态成员变量
}
static int m_A; //静态成员变量
int m_B; //
private:
//静态成员函数也是有访问权限的
static void func2()
{cout<< "func2调用"<< endl;
}
};
int Person::m_A = 10;
void test01()
{//静态成员变量两种访问方式
//1、通过对象
Person p1;
p1.func();
//2、通过类名
Person::func();
//Person::func2(); //私有权限访问不到
}
int main()
{test01();
system("pause");
return 0;
}
四、类成员储存方式在C++中,类中的静态成员变量和静态成员函数都不占用对象空间;普通函数也不占用对象空间,所有函数共享一个函数实例。所以只有非静态成员变量才属于类的对象上。
示例代码:
class Person
{public:
Person()
{mA = 0;
}
//非静态成员变量占对象空间
int mA;
//静态成员变量不占对象空间
static int mB;
//函数也不占对象空间,所有函数共享一个函数实例
void func()
{cout<< "mA:"<< this->mA<< endl;
}
//静态成员函数也不占对象空间
static void sfunc()
{}
};
int main()
{cout<< sizeof(Person)<< endl;
system("pause");
return 0;
}
五、this指针C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
this指针是隐含每一个非静态成员函数内的一种指针。
this指针不需要定义,直接使用即可。
this指针的用途:
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
代码示例:
#includeusing namespace std;
class Person
{public:
//当形参和成员变量同名时,可用this指针来区分
Person(int age)
{this->age= age;
}
// 在类的非静态成员函数中返回对象本身,实现相加操作
Person& PersonAdd(Person &p)
{this->age+= p.age;
return *this;
}
int age;
};
int main()
{Person p1(10);
Person p2(10);
p2.PersonAdd(p1).PersonAdd(p1).PersonAdd(p1).PersonAdd(p1).PersonAdd(p1);
cout<< p2.age<< endl;
system("pause");
return 0;
}
六、const修饰成员函数常函数:
- 成员函数后加const后我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加const称该对象为常对象
- 常对象只能调用常函数
代码示例:
#includeusing namespace std;
class Person
{public:
//定义一个常函数
void fun1() const
{//this->m_A = 1;//错误,常函数内不可以修改成员属性
this->m_B = 10; //成员属性声明时加关键字mutable后,在常函数中依然可以修改
}
//定义一个普通函数
void fun2()
{}
int m_A;
mutable int m_B;
};
//常函数示例
void test1()
{Person p;//普通对象可调用常函数
p.fun1();
}
//常对象示例
void test2()
{const Person p;//定义一个常对象
p.fun1();//常对象调用常函数
//p.m_A = 10;//错误,常对象不能修改成员变量的值,但是可以访问
//p.fun2();//错误,常对象只能调用常函数
p.m_B = 1; //常对象可以修改mutable修饰成员变量
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网页题目:C++学习笔记——类和对象-创新互联
标题URL:http://ybzwz.com/article/cscscj.html