类的创建、赋值、权限
C++面向对象的三大特性为:封装、继承、多态
语法:class 类名{ 访问权限: 属性 / 行为 };访问权限
公共权限 public 成员 类内可以访问,类外可以访问
保护权限 protected 成员 类内可以访问,类外不可访问 子类可以访问父类中的保护内容
私有权限 private 成员 类内可以访问,类外不可访问 子类不以访问父类中的私有内容stuct和class的比较
在C++中 struct和class唯一的区别就在于默认的访问权限不同struct 默认权限为公共 class 默认权限为私有封装:成员属性设置为私有
优点:将所有成员属性设置为私有,可以自己控制读写权限
对于写权限,我们可以检测数据的有效性注意:同一个项目下结构体之间,类之间,结构体和类之间的命名都要不同
#include<iostream>
#include<string>
using namespace std;
class stu {
public:
//属性 成员属性 成员变量
string sName;
int sId;
//行为 成员函数 成员方法
void set_sName(string name) {
sName = name;
}
void set_sId(int id) {
sId = id;
}
void showprint() {
cout << "name:" << sName << "\tid:" << sId << endl;
}
};
class tea {
//属性
public:
string tName;
protected:
int tId;
private:
int tWage;
//方法
public:
void set_tea(string name, int id, int wage) {
this->tName = name;
this->tId = id;
this->tWage = wage;
}
void showprint() {
cout << "name:" << tName << "\tid:" << tId << "\t\twage:" << tWage << endl;
}
};
struct test1 {
int struct_test = 10;//struct 默认权限为公共
};
class test2 {
int class_test = 10;//class 默认权限为私有
};
void main() {
stu tony;
//public类可以这样赋值
//tony.sName = "tony";
//tony.sId = 123;
tony.set_sName("tony");
tony.set_sId(123);
tony.showprint();
tea mary;
mary.set_tea("mary",101,1000);
mary.showprint();
test1 a;
a.struct_test = 0;//可访问
test2 b;
//b.struct_test = 0;//不可访问
}
类和对象:初始化和清理
构造函数:类名(){数据类型 参数}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数:~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
如果不提供构造函数和析构函数,编译器会提供空实现的构造函数和析构函数。
class Person {
public://构造函数和析构函数默认是私有的,所以要加public
Person(){//程序在调用对象时候会自动调用构造
cout << "我是Person的构造函数" << endl;
}
~Person() {//程序在对象销毁前会自动调用析构
cout << "我是Person的析构函数" << endl;
}
};
void test(){
Person p;
}
void main(){
test();
system("pause");
Person person;
system("pause");
}
构造函数的分类及调用
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造三种调用方式:
括号法
显示法
隐式转换法
注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明(返回类型 函数名(),Person p()——错误;)
注意2:不能利用拷贝构造函数初始化匿名对象编译器认为是对象声明
#include<iostream>
using namespace std;
class Person {
public:
int age;
int score;
//普通构造
Person() {//无参构造
cout << "无参构造函数!" << endl;
this->age = 0;
this->score = 0;
}
Person(int age, int score) {//有参构造
cout << "有参构造函数!" << endl;
this->age = age;
this->score = score;
}
//拷贝构造
Person(const Person &p) {
cout << "拷贝构造函数!" << endl;
this->age = p.age;
this->score = p.score;
}
void Show() {
cout << "age:" << age << "\tscore:" << score << endl;
}
//~Person() {
// cout << "析构函数!" << endl;
//}
};
//调用有参的构造函数
void test() {
//括号法,常用
cout << "*****括号法,常用*****" << endl;
Person p11;
p11.Show();
Person p12(20,80);
p12.Show();
Person p13(p12);
p13.Show();
//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明——返回类型 函数名(),Person p()——错误;
//显式法
cout << endl << "*****显式法*****" << endl;
Person p21 = Person();
p21.Show();
Person p22 = Person(20,80);
p22.Show();
Person p23 = Person(p22);
p23.Show();
//Person(10)单独写就是匿名对象 当前行结束之后,马上析构
//隐式转换法
cout << endl << "*****隐式转换法*****" << endl;
Person p31 = { };//元素为空或者有多个元素时要用{ },只有一个元素时可以不用{ }直接写元素
p31.Show();
Person p32 = { 10, 80 };
p32.Show();
Person p33 = p32;
p33.Show();
//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
//Person p5(p4);
}
void main() {
test();
}
拷贝构造函数的调用时机
1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.以值方式返回局部对象
#include<iostream>
using namespace std;
class Person {
public:
Person() {
cout << "无参构造函数!" << endl;
this->age = 0;
}
Person(int age) {
cout << "有参构造函数!" << endl;
this->age = age;
}
Person(const Person& p) {
cout << "拷贝构造函数!" << endl;
this->age = p.age;
}
//析构函数在释放内存之前调用
~Person() {
cout << "析构函数!" << endl;
}
public:
int age;
};
//1. 使用一个已经创建完毕的对象来初始化一个新对象
void test_a() {
cout << "*****1.使用一个已经创建完毕的对象来初始化一个新对象" << endl;
Person man(100); //p对象已经创建完毕
Person newman(man); //调用拷贝构造函数
Person newman2 = man; //拷贝构造
//Person newman3;
//newman3 = man; //不是调用拷贝构造函数,赋值操作
}
//2. 值传递的方式给函数参数传值
//相当于Person p1 = p;
void doWork(Person p1) {}
void test_b() {
cout << endl << "*****2.值传递的方式给函数参数传值" << endl;
Person p; //无参构造函数
doWork(p);
}
//3. 以值方式返回局部对象
Person doWork2(){
Person p1;
cout << (int*)&p1 << endl;
return p1;
}
void test_c(){
cout << endl << "*****3.以值方式返回局部对象" << endl;
Person p = doWork2();
cout << (int*)&p << endl;
}
void main() {
test_a();
test_b;
test_c();
}
构造函数调用规则
创建一个类时,默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝构造函数调用规则如下:
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不会再提供其他构造函数(无参构造,有参构造,构造函数)
#include<iostream>
using namespace std;
class Person {
public:
//无参(默认)构造函数
Person() {
cout << "无参构造函数!" << endl;
}
//有参构造函数
Person(int a) {
age = a;
cout << "有参构造函数!" << endl;
}
//拷贝构造函数
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数!" << endl;
}
//析构函数
~Person() {
cout << "析构函数!" << endl;
}
public:
int age;
};
void test_a()
{
Person p1(18);
//如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
Person p2(p1);
cout << "p2的年龄为: " << p2.age << endl;
}
void test_b()
{
//如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
Person p1; //此时如果用户自己没有提供默认构造,会出错
Person p2(10); //用户提供的有参
Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供
//如果用户提供拷贝构造,编译器不会提供其他构造函数
Person p4; //此时如果用户自己没有提供默认构造,会出错
Person p5(10); //此时如果用户自己没有提供有参,会出错
Person p6(p5); //用户自己提供拷贝构造
}
void main() {
test_a();
test_b();
}
深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作
不管对象的属性是什么数据类型,都是直接拷贝一份,如果是指针的话,其实只拷贝了地址,并没有复制数据
对于指针,直接复制指针,没有复制数据(两个对象中的height指针存放的地址相同,指向同一个内存空间)
深拷贝:在堆区重新申请空间,进行拷贝操作
对于指针,先在堆区域复制一个原对象指针指向的数据,再将新对象的指针指向该数据(两个对象中的height指针存放的地址不同,但两个地址的数据相同)
对象中成员含指针时,建议用深拷贝
#include<iostream>
using namespace std;
class Person {
public:
int age;
int *height;
Person() {
cout << "无参构造" << endl;
}
Person(int age, int height) {
cout << "有参构造" << endl;
this->age = age;
this->height = new int(height);//new是在堆区重新开辟一个空间,并返回的是该数据类型的指针
}
Person(const Person &p) {
cout << "拷贝构造" << endl;
this->age = p.age;
//浅拷贝,对于指针,直接复制指针,没有复制数据(两个对象中的height指针存放的地址相同,指向同一个内存空间)
//this->height = p.height;//用浅拷贝的时候,在两个对象的指针都释放的时候会报错
//深拷贝,对于指针,先在堆区域复制一个原对象指针指向的数据,再将新对象的指针指向该数据(两个对象中的height指针存放的地址不同,但两个地址的数据相同)
this->height = new int(*p.height);//用深拷贝时,对象含指针释放的时候也不会报错
}
~Person() {
cout << "析构函数!" << endl;//析构函数将堆区域的变量释放
if (height != NULL)
{
delete height;
height = NULL;//置空,防止野指针出现
}
}
};
void test() {
Person person(24, 180);
cout << "person\t\tage:" << person.age << "\thegiht" << *person.height << endl;
Person new_person(person);
cout << "new_person\tage:" << person.age << "\thegiht" << *person.height << endl;
}
void main() {
test();
}
初始对象
方法一:利用构造函数,写在构造函数里面,一个一个成员的赋值
方法二:构造函数():属性1(值1),属性2(值2)… {}
#include<iostream>
using namespace std;
class Person {
public:
int age;
int height;
int score;
Person(int a,int b,int c) :age(a), height(b), score(c) {//方法二初始化列表
}
void showprint() {
cout << "age:" << age << "\t\theight:" << height << "\tscore:" << score << endl;
}
};
void main() {
Person person(20,180,85);
person.showprint();
}
对象成员
类作为另一个类的成员时,我们称该成员为对象成员
构造的顺序是 :先调用对象成员的构造,再调用本类构造
析构顺序与构造相反
#include<iostream>
#include<string>
using namespace std;
class Phone {
public:
string pname;
int price;
Phone() {}
Phone(string a, int b) :pname(a), price(b) {
cout << "phone构造函数" << endl;
}
};
class Person {
public:
string name;
Phone myphone;
Person(string a, string b, int c) :name(a), myphone(b, c) {//这里myphone(b, c)是一个隐式转换法,等价Phone myphone={b,c}
cout << "person构造函数" << endl;
}
};
void test() {
Person person("张三", "华为", 3200);
cout << "name:" << person.name << "\t\tphone:" << person.myphone.pname << "\t\tprice:" << person.myphone.price << endl;
}
void main() {
test();
}
静态成员,静态方法
静态成员变量
1.所有对象共享同一份数据
2.在编译阶段分配内存
3.类内声明,类外初始化(重点)静态成员函数
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
static int static_a;
int nostatic;
static void fuc_a() {
cout << "静态方法fuc_a(puplic)" << endl;
//nostatic = 100;//静态函数只能访问静态成员,非静态成员的静态函数不能访问
}
private:
static int static_b;
static void fuc_b() {
cout << "静态方法fuc_b(private)" << endl;
}
};
int Person::static_a = 0;
int Person::static_b = 0;
void test() {
//静态成员变量和静态成员方法两种访问方式
//1、通过对象
Person person;
person.static_a = 100;
//person.static_b = 100;//私有的静态成员不可访问
cout << "static_a = " << person.static_a << endl;
person.fuc_a();
//2、通过类名
cout << "static_a = " << Person::static_a << endl;
Person::fuc_a();
//Person::fuc_b();//私有的静态方法不可访问
}
void main() {
test();
}
类和对象的大小
C++中类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上、空类1B,非空类(静态成员变量的大小相加)
#include<iostream>
using namespace std;
class Person_a {};//1B
class Person_b {//4B
int a;//非静态成员变量
};
class Person_c {//4B
int a;//非静态成员变量
static int static_c;//静态成员变量
};
int Person_c::static_c = 0;
class Person_d {//4B
int a;//非静态成员变量
static int static_d;//静态成员变量
void func_() {}
};
void test() {
//编译器会给空对象分配一个字节空间,为了区分空对象占内存的位置
cout << "空类占字节数:" << sizeof(Person_a) << endl;
//非静态成员变量 属于类的对象上
cout << "(只含非静态成员变量)类占字节数:" << sizeof(Person_b) << endl;
//静态成员变量 不属于类的对象上
cout << "(非静态成员变量+静态成员变量)类占字节数:" << sizeof(Person_c) << endl;
//成员函数 不属于类的对象上
cout << "(非静态成员变量+静态成员变量+函数)类占字节数:" << sizeof(Person_d) << endl;
}
void main() {
test();
}
this指针
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可this指针的用途:
当形参和成员变量同名时,可用this指针来区分
在类的非静态成员函数中返回对象本身,可使用return *this
C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针
const修饰函数和对象
常函数:
成员函数后加const后我们称为这个函数为常函数
常函数内不可以修改成员属性
成员属性声明时加关键字mutable后,在常函数中依然可以修改常对象:
声明对象前加const称该对象为常对象
常对象只能调用常函数
#include<iostream>
using namespace std;
class Person {
public:
public:
int m_A;
mutable int m_B; //可修改 可变的
Person() {
m_A = 0;
m_B = 0;
}
//this指针的本质是一个指针常量,指针的指向不可修改
//如果想让指针指向的值也不可以修改,需要声明常函数
void ShowPerson() const {
//const Type* const pointer;
//this = NULL; //不能修改指针的指向 Person* const this;
//this->mA = 100; //但是this指针指向的对象的数据是可以修改的
//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
this->m_B = 100;
cout << "常函数" << endl;
}
void MyFunc() {
//mA = 10000;
cout << "普通函数"<< endl;
}
//const修饰对象 常对象
};
void test() {
const Person person; //常量对象
cout << person.m_A << endl;
//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
person.m_B = 100; //但是常对象可以修改mutable修饰成员变量
person.ShowPerson();//常对象只能调用常函数
//常对象访问成员函数
//person.MyFunc(); //常对象不能调用普通函数
Person person11;
}
void main() {
test();
}
友元
在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字为 friend
友元的三种实现
1.全局函数做友元 friend void myfriend();
2.类做友元 friend class friend_a;
3.成员函数做友元 friend void friend_b::visit1();
注意: 1.用指针(或引用)创建类,可以解决类未定义的问题(先声明)
2.把类中所有的函数先声明,放在最后定义,可以解决未定义的问题
#include<string>
using namespace std;
class Building;
class friend_a;
class friend_b;
class friend_a {//友元类
public:
friend_a();
void visit();
private:
Building *b;
};
class friend_b {//友元函数的类
public:
friend_b();
void visit1();//友元函数
void visit2();//非友元函数
private:
Building *b;
};
class Building {//本类
//1.全局函数做友元,告诉编译器 myfriend全局函数是 Building类的友元,可以访问类中的私有内容
friend void myfriend();
//2.类做友元,友元类内的函数可以访问该类的私有容
friend class friend_a;
//3.成员函数做友元,该成员函数可以访问该类的私有容
friend void friend_b::visit1();
public:
string sittingroom;
private:
string bedroom;
public:
Building();
};
friend_a::friend_a() {
b = new Building;
}
friend_b::friend_b() {
b = new Building;
}
Building::Building() {
sittingroom = "客厅";
bedroom = "卧室";
}
void friend_a::visit() {//2.类做友元
cout << "我的朋友正在访问:" << b->sittingroom << endl;
cout << "我的朋友正在访问:" << b->bedroom << endl;//friend_类是 Building类的友元,可以访问类中的私有内容
}
void friend_b::visit1() {
cout << "我的朋友正在访问:" << b->sittingroom << endl;
cout << "我的朋友正在访问:" << b->bedroom << endl;//friend_b::visit1()成员函数是Building类的友元,可以访问类中的私有内容
}
void friend_b::visit2() {
cout << "我的朋友正在访问:" << b->sittingroom << endl;
//cout << "我的朋友正在访问:" << b->bedroom << endl;//friend_b::visit2()成员函数不是Building类的友元,不可以访问类中的私有内容
}
void myfriend() {//1.全局函数做友元
Building see;
cout << "我的朋友正在访问:" << see.sittingroom << endl;
cout << "我的朋友正在访问:" << see.bedroom << endl;//myfriend全局函数是 Building类的友元,可以访问类中的私有内容
}
void main() {
myfriend();
friend_a fria;
fria.visit();
friend_b frib;
frib.visit1();
frib.visit2();
}
本文介绍了C++中的面向对象编程特性,包括类的创建、赋值和权限控制。详细讲解了public、protected、private的访问权限,struct与class的区别,以及封装的概念和优点。此外,还阐述了构造函数和析构函数的作用,无参构造、有参构造和拷贝构造的调用规则,并讨论了深拷贝与浅拷贝的区别。最后,提到了静态成员、this指针、常量对象和友元的相关知识。
1081

被折叠的 条评论
为什么被折叠?



