访问修饰符 | 类内部 | 子类 | 普通外部类 |
---|---|---|---|
public | √ | √ | √ |
protected | √ | √ | × |
private | √ | × | × |
默认访问权限
class默认为private
类名(){}
class Person{
//无参构造函数
Student();
//有参构造函数
Student(string name, int age);
//有参构造函数
Student(int age);
//拷贝构造函数
Student(const Student& stu);
string toString();
//析构函数
~Student();
}
//拷贝构造函数举例
Person(const Person& p){
this->age = stu.age;
this->name = stu.name;
}
//拷贝构造函数调用情景
//1.使用已存在对象初始化新对象
Person p1("xiaohong",14);
Person p2(p1);
//2.函数值传递复制参数的时候会调用拷贝构造函数
void func(Person p){
}
int main(){
Person p1("xiaohong",14);
func(p1);
}
//3. 值方式返回局部对象
Person func(){
Person p("Lisi",20);
// 返回的Person对象是P的拷贝
return p;
}
int main(){
Person p1("xiaohong",14);
Person p2=func();
}
<类名():参数1(初始值)>[,参数2(初始值),...]
代码示例
//
Person():age(12),name("Lisi"){
}
Person(){
this->age = 12;
this->name = "Lisi";
}
//
Person(string myName , int myAge):name(myName),age(myAge){
}
Person((string myName , int myAge){
this->age = myAge;
this->name = myName;
}
Person p1; //无参数构造函数,不要加括号
Person p1(); //编译器会认为这是一个函数p1()的声明
Person p2("xiaohng",10); //有参数构造函数
Person p3(p2); //拷贝构造函数
Person p1;//无参数构造函数
Person p1 = Person("xiaohng",10);//有参数构造函数
Person p3 = Person(p2);//拷贝构造函数
//相关知识点 匿名对象
Person("xiaohng",10);
//不要使用拷贝函数 初始化匿名对象
//示例
Person(p3); //报错,Person p3重定义
//原因: Person(p3)语句 相当于 Person p3;
Person p1; //无参数构造函数
Person p2 = 10; //有参数构造函数== Person p2(10)
Person p3 =p2; //拷贝构造函数==Person(p2)
~类名(){}
代码说明
//测试类1
class Person {
};
Person p;
// sizeof(p)=1;
//类内部为空的对象大小为1字节
//测试类2
class Person {
int age;
};
Person p;
//sizeof(p)=4;
//测试类3
class Person {
int age;
static int count;
};
Person p;
//sizeof(p)=4;
//测试类4
class Person {
int age;
static int count;
void func();
};
Person p;
//sizeof(p)=4;
//测试类5
class Person {
int age;
static int count;
void func();
static void func2()
};
Person p;
// sizeof(p)=4;
静态成员变量声明之后,需要在类的外部分配空间
代码示例
class Person{
public:
static int count;
}
int Person::count=0;
*this
解引用为当前对象Person * const this
使用this实现链式编程示例
class Timer
#include
using namespace std;
class Timer
{
private:
int sec;
int min;
int hour;
public:
Timer():sec(0),min(0),hour(0) {
}
Timer(int sec, int min, int hour) {
this->sec = sec;
this->min = min;
this->hour = hour;
}
//下面三个函数返回当前对象的引用
//一定要返回当前对象的引用,否则会返回当前对象的拷贝,无法对当前对象实现链式操作
Timer& addSec(int sec=0) {
this->sec += sec;
this->min += this->sec / 60;
this->sec = this->sec % 60;
return *this;
}
Timer& addMin(int min=0) {
this->min += min;
this->hour += this->min / 60;
this->min = this->min % 60;
return *this;
}
Timer& addHour(int hour=0) {
this->hour += hour;
return *this;
}
string toString() {
return "[" + to_string(hour) + ":" + to_string(min) + ":" + to_string(sec) + "]";
}
};
main.cpp
int main() {
Timer timer(0, 0, 0);
timer.addSec(10).addMin(20).addHour(100); //链式
cout << timer.toString() << endl;
timer.addSec(20).addMin(30).addHour(1); //链式
cout << timer.toString() << endl;
timer.addSec(50).addMin(10).addHour(0); //链式
cout << timer.toString() << endl;
return 0;
}
在c++中,空指针可以访问没有使用非静态成员变量的函数、
示例代码
class Person{
private:
int age;
public:
void sayHello(){
cout<<"hello world!"<
为了程序的健壮性,可以将上面的Person
类改为下面的代码
class Person{
private:
int age;
public:
void sayHello(){
cout<<"hello world!"<
this的本质
指针常量 Person * const this
,在使用this
指针时,this指针的指向不可以改变,例如下面的代码是错误的
class Person{
public:
void change(){
this == NULL; //错误
}
}
但是指针指向的值时可以改变的,例如
class Person{
int age;
public:
void change(){
this->age = 10; //正确
}
}
使用const修饰成员函数,相当于将this
修改为const Person * const this
,即this
指针的指向和指向对象的值都不可以改变,因此在常函数体内部无法修改当前对象的属性。
但是使用mutable
修饰的属性值在常函数内部可以改变
常函数声明格式
class Classname{
//常函数声明
func() const{
}
}
示例代码
class Person{
private:
int age;
mutable string name;
public:
void testChange() const{
this->age = 10; //错误
this->name = "root"; //正确
}
}
mytable
修饰的除外)代码示例
class Person{
public:
int age;
mutable string name;
public:
//常函数声明
void testChange() const{
this->age = 10; //错误
this->name = "root"; //正确
}
//普通成员函数
void changeAge(){
age=12;
}
}
int main(){
//常对象声明
const Person p;
p.age = 12; //错误
p.name = "Tom"; //正确
p.testChange(); //正确
p.changeAge(); //错误
}
一般情况下,类中private
属性在类的外部不能被访问,但是一个类的友元
可以不受这个限制。
三种友元的方式
使用全局函数作为友元时,只需要在类的声明中加入friend void func();
即可将一个全局函数声明为一个类的友元
代码示例
class Person
#pragma once
#include
#include
using namespace std;
class Person
{
//友元全局函数声明
friend void askSecret(Person &p);
public:
string name;
int age;
private:
string secret; //秘密,只能告诉好朋友,一般人不能访问
public:
Person( string name,int age) {
this->name = name;
this->age = age;
}
void setSecret(string str) {
this->secret = str;
}
};
MainApp.cpp
#include"Person.h"
void askSecret(Person& p) {
//类的外部访问私有属性 p.secret
cout << p.name << "'s secret is " << p.secret << endl; //tom's secret is I like Jess
}
int main() {
Person p("tom", 12);
p.setSecret("I like Jess");
askSecret(p);
system("pause");
return 0;
}
使用类作为友元时,只需要在类的声明中加入friend class
即可将一个类声明为另一个类的友元
代码示例
class Person
#include
#include
using namespace std;
class Person
{
//声明友元类
friend class Doctor;
public:
string name;
int age;
private:
string secret; //秘密,只能告诉好朋友,一般人不能访问
public:
Person( string name,int age) {
this->name = name;
this->age = age;
}
void setSecret(string str) {
this->secret = str;
}
};
class Doctor
#include"Person.h"
class Doctor
{
string name;
public:
Doctor() {
this->name = "doctor li";
}
void askSecret(Person& patient) {
//友元访问Person类的私有属性
cout << patient.name << "'s secret is \"" << patient.secret << "\"" << endl;
}
};
MainApp.cpp
#include"Person.h"
#include "Doctor.h"
int main() {
Person p("tom", 12);
p.setSecret("I like Jess");
Doctor doctor;
doctor.askSecret(p); //tom's secret is "I like Jess"
system("pause");
return 0;
}
使用成员函数作为友元时,只需要在类的声明中加入friend void
即可将一个成员函数声明为另一个类的友元
注意
:尽量不要使用这种做法,会造成 A&B两个类互相包含的错误。例如,A.func()是B的友元,在声明时,需要在B中包含A,但是在A中访问B的时候,又必须包含B。
声明方式
//成员函数实现
Timer operator+(Timer t) {
Timer tmp(t);
tmp.addSec(this->sec).addMin(this->min).addHour(this->hour);
return tmp;
}
//全局函数实现
Timer tmp(t1);
tmp.addSec(t2.getSec()).addMin(t2.getMin()).addHour(t2.getHour());
return tmp;
//调用方式
Timer t1(10,20,30);
Timer t2(20,40,50);
Timer t4 = t1 + t2; // 本质为 Timer t3=t1.operator+(t2);
+
重载代码示例
class Timer
#include
using namespace std;
class Timer
{
private:
int sec;
int min;
int hour;
public:
Timer():sec(0),min(0),hour(0) {
}
//运算符重载声明
Timer operator+(Timer t) {
Timer tmp(t);
tmp.addSec(this->sec).addMin(this->min).addHour(this->hour);
return tmp;
}
Timer(int sec, int min, int hour) {
this->sec = sec;
this->min = min;
this->hour = hour;
}
Timer& addSec(int sec=0) {
this->sec += sec;
this->min += this->sec / 60;
this->sec = this->sec % 60;
return *this;
}
Timer& addMin(int min=0) {
this->min += min;
this->hour += this->min / 60;
this->min = this->min % 60;
return *this;
}
Timer& addHour(int hour=0) {
this->hour += hour;
return *this;
}
string toString() {
return "[" + to_string(hour) + ":" + to_string(min) + ":" + to_string(sec) + "]";
}
};
MainApp.cpp
int main() {
Timer timer(0, 0, 0);
timer.addSec(10).addMin(20).addHour(100);
Timer timer2(10, 50, 40);
//Timer timer3 = timer.operator+(timer2);
Timer timer3 = timer + timer2;
cout << timer.toString() << endl;
cout << timer2.toString() << endl;
cout << timer3.toString() << endl;
system("pause");
return 0;
}
++
运算符区分++int
和int++
的方法
//++i
operator++(){
}
// i++
operator++(int){
}
=
赋值运算符重载堆
区的数据,即使用new
关键字开辟的内存空间,使用赋值语句赋值时会产生浅拷贝
的问题,这个时候,对=
·运算符重载非常有必要==
&!=
重载()
函数调用运算符重载(仿函数)代码示例
class Printer
#include
#include
using namespace std;
class Printer
{
public:
//这里对“()”进行重载
void operator()(string text) {
cout << text << endl;
}
};
MainApp.cpp
int main() {
Printer print;
//使用`对象()`的方式可以调用仿函数
print("Hello world!!");
//匿名对象: 类名()
Printer()("这里是匿名对象函数打印结果");
return 0;
}
语法class 类名: <继承方式> <父类>
继承方式
父类属性权限 | public | protected | private |
---|---|---|---|
public继承 | public | protected | 不可访问 |
protected继承 | protected | protected | 不可访问 |
private继承 | private | private | 不可访问 |
代码示例
class Base{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
}
class Son: public Base{
public:
int m_D;
}
int main(){
cout<
创建一个子类对象时:
释放子类对象时:
class Animal(){
public:
string getName(){
}
}
class Dog:public Animal(){
public:
string getName(){
}
}
int main(){
Dog d;
d.getName(); //调用子类函数
d.Animal::getName(); //调用父类函数
}
虚继承语法
class name: virtual public base{}
代码示例
没有使用虚继承
class A {
public:int age;
};
class B :public A {
};
class C : public A {
};
class D :public B, public C {
};
int main() {
D d;
d.C::age = 18;
d.B::age = 20;
// d.age = 20; 编译错误
cout << "d.C::age=" << d.C::age<
class A {
public:int age;
};
//虚继承
class B :virtual public A {
};
//虚继承
class C : virtual public A {
};
class D :public B, public C {
};
int main() {
D d;
d.C::age = 18;
d.B::age = 20;
d.age = 30; //可以使用,没有编译错误
cout << "d.C::age=" << d.C::age<
virtual void func()
代码示例
classAnimal
class Animal
{
public:
//虚函数
virtual void speak() {
cout << "animal is speaking..." << endl;
}
};
class Dog
class Dog : public Animal
{
void speak() {
cout << "dog is speaking" << endl;
}
};
MainApp.cpp
void test(Animal & a) {
a.speak();
}
int main() {
Dog d;
test(d); //dog is speaking
}
virtual returnType func(param)=0
使用多态时,利用父类类型接受子类对象
Animal animal = new Dog;
当释放这个对象时,会调用父类的析构函数,子类的析构函数不会被调用,导致子类中有释放堆区数据的代码不能被执行,内存释放不干净
解决方法:将父类的析构函数写成虚析构
函数
virtual ~Animal(){
}
这样在释放子类对象时,会先调用子类的析构函数,再调用父类的析构函数
纯虚析构
virtual ~Animal()=0;
//类的外部
Animal::~Animal(){
}