对象:万事万物皆为对象;
类:具有相同性质的对象,可以抽象为类。
(1)将属性和行为作为一个整体,表现生活中的事物。
(2)将属性和行为加以权限控制。
注:叫法:
类中的属性和行为:统称成员
属性:又称为 成员属性、成员变量
行为:又称为 成员函数、成员方法
class 类名 { 访问权限 :属性 / 行为 } ;
实例化:通过一个类,创建出一个对象。
例:设计一个圆类,求圆的周长:
//设计一个圆类,求圆的周长
const double PI = 3.14;
class Circle
{
//访问权限
public:
//属性
int r;
//行为
double C()
{
return 2 * PI * r;
}
};
int main()
{
Circle round;//实例化
round.r = 10;
cout << "圆的周长:" << round.C() << endl;
return 0;
}
公共权限(public):类内可以访问,类外可以访问
保护权限(protected): 类内可以访问,类外不可以访问,子类可访问父类中保护内容
私有权限(private): 类内可以访问,类外不可以访问,子类不可访问父类中私有内容
(保护和私有在继承中有区别)
例:
class Person
{
public:
string Name;
protected:
string car;
private:
int Password;
public:
void func()
{
Name = "name";
car = "car";
Password = 1;//类内均可访问
}
};
int main()
{
Person p1;
p1.Name = "zhangsan";
p1.car = "benchi";//报错,保护和私有访问不到
return 0;
}
基本一致,但默认的访问权限不同。
struct的默认权限为公共,而class的默认权限为私有。
(默认权限,即未标明访问权限时,创建成员时的成员权限)
将成员属性设为私有,提供公共的函数来读写私有成员。
优点:
1.可以自己控制读写权限;
2.对于写,可以检测数据有效性
//人类
class Person
{
public:
//设置姓名
void setName(string name)
{
m_Name = name;
}
//获取姓名
string getName()
{
return m_Name;
}
//获取年龄
int getAge()
{
return m_Age;
}
//设置偶像
void setIdol(string Idol)
{
m_Idol = Idol;
}
//设置年龄
void setAge(int Age)
{
if (Age < 0 || Age>150)
{
cout << "年龄有误,写入失败" << endl;
return;
}
m_Age = Age;
}
private:
string m_Name;//姓名 可读可写
int m_Age = 18;//年龄 可读 也可以写但只限制在0到100(检测数据有效性)
string m_Idol;//偶像 只写
};
构造函数:为对象的成员属性赋值。
析构函数:对象销毁前进行清理。
c++利用这两个函数进行对象的初始化和清理,必须有,如果程序员不实现,会由编译器自动实现,但是是空实现。(即函数中没有内容)
注:构造函数和析构函数应放在public里。
1.没有返回值也不写void;
2.函数名称与类名相同;
3.构造函数可以有参数,因此可以重载;
4.构造函数会由编译器自动调用,无需手动调用。
1.没有返回值也不写void;
2.函数名称与类名相同,前加 ~;
3.构造函数不可以有参数,因此不可以重载;
4.构造函数会由编译器自动调用,无需手动调用。
两种分类方式:
按参数分类:有参构造和无参构造
按类型分类:普通构造和拷贝构造
三种调用方式:
括号法,显示法,隐式转换法。
Person()
{
cout << "构造函数的调用" << endl;
}
Person(int a)
{
cout << "构造函数的调用" << endl;
}
传入一个对应类的引用,同时必须加const。
Person(const Person &p)
{
age = p.age;
}
用括号传参的方式决定用哪一种构造函数。
//括号法
Person p1;//默认无参构造
Person p2(10);//有参构造
Person p3(p2);//引用构造
注:用无参构造时不要加(),编译器会默认为函数声明,而非创建对象。
//显示法
Person p1;
Person p2 = Person(10);//有参构造
Person p3 = Person(p2);//拷贝构造
注:1.匿名对象, 会在本行执行后就被回收掉。
Person(10);
2.不要利用拷贝构造函数初始化匿名对象,因为会被认为是一个对象的声明,重定义报错。
Person(p3);
//隐式转换法
Person p4 = 10;
Person p5 = p4;
1.使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
Person p1(10);
Person p2(p1);
}
2.值传递的方式给函数参数传值
void work(Person p)//拷贝构造
{
}
void test02()
{
Person p;
work(p);
}
1.默认情况下,c++编译器至少给一个类添加3个函数:
(1)默认构造函数(无参,函数体为空)
(2)默认析构函数(无参,函数体为空)
(3)默认拷贝构造函数,对属性进行值拷贝(即不写拷贝构造函数也有默认的)
2.如果写了有参构造函数,编译器就不再提供默认构造函数,但依然有拷贝构造。
如果写了拷贝构造函数,编译器就不提供其他普通构造函数了。
无参 < 有参 < 拷贝
class Person
{
public:
Person(int a)
{
cout << "Person有参构造函数调用" << endl;
}
~Person()
{
cout << "Person析构函数调用" << endl;
}
int m_age;
};
int main()
{
Person p;//报错,不再有默认构造函数
return 0;
}
浅拷贝:纯赋值拷贝
深拷贝:在堆区重新申请空间,进行拷贝操作。
注:默认拷贝构造函数为浅拷贝,会出现如下问题。
class Person
{
public:
Person(int a,int b)
{
m_age = a;
m_height = new int(b);
cout << "Person有参构造函数调用" << endl;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "Person析构函数调用" << endl;
}
int m_age;
int* m_height;
};
int main()
{
Person p1(18, 160);
Person p2(p1);//此处使用默认拷贝构造,为浅拷贝,将p1.m_height的地址直接拷贝给p2,后来析构时就delete两次了
return 0;
}
解决办法:自己写一个深拷贝构造函数:
Person(const Person& p)
{
m_age = p.m_age;
m_height = new int(*p.m_height);
}
用途:初始化属性。
语法:构造函数():属性1(值1),属性2(值2)。。。{ }
class Person
{
public:
Person(int a,int b) :A(a), B(b) {
}
int A;
int B;
};
类中的成员可以是另一个类的对象,该成员叫做对象成员。
class A{};
class B {
A a;
};
注:
1.构造时,先构造内对象,然后外对象。
析构时,先析构外对象,再析构内对象。
2.在初始化列表给对象成员的属性赋值时,可以直接用属性对应的数据类型赋值
(不好说明,请看案例):
class Person
{
public:
Person(int a)
{
age = a;
}
int age;
//string name;
};
class class1
{
public:
int personcount;
Person p;
class1(int cou, int age) :personcount(cou), p(age){}//注意这里
};
int main()
{
class1 c(50, 18);
cout << c.personcount << c.p.age << endl;
return 0;
}
注意Person类型的 p 用 int 类型的age初始化,相当于 Person p = age ,是隐式转换法,但是内对象的类需要有有参构造函数。
静态成员就是在成员变量和成员函数前加上关键字static,称之为静态成员。
1.所有对象共享同一份数据(同一块地址,一个修改另一个也改)
2.在编译阶段分配内存
3.类内声明,类外初始化(要在函数之外初始化)
int Person::A = 100;
int main() {
Person p;
cout << p.A << endl;
return 0;
}
注:
1.静态成员变量有两种访问方式:对象,类名
Person p;
cout << p.A << endl;//对象
cout << Person::A << endl;//类名
2.类外无法访问私有静态成员变量,但也可在函数之外初始化。
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
class Person
{
public:
static void func()
{
cout << "static void func调用" << endl;
a = 100;
b = 200;//报错,无法访问
}
static int a;
int b;
};
注:
1.两种访问方式:对象,类名
Person p;
p.func();
Person::func();