类和对象(C witch class)
面向过程编程
面向对象编程
class 类
C++的复合数据类型
class Student
{
public:
//成员函数(方法)
void show(); //在类中声明
void SetAge(int age);
void SetName(std::string name);
private:
//成员变量(属性)
std::string m_name; //为了避免与函数中的形参命名冲突,自己的成员前面一般加 _m
int m_age;
};
//函数在类外定义,但注意,需要在函数前加类的声明,Student:: 标明函数是Student类中
void Student::show()
{
std::cout << "学生姓名:" << m_name << " 学生年龄:" << m_age << std::endl;
}
void Student::SetAge(int age)
{
m_age = age;
}
void Student::SetName(std::string name)
{
m_name = name;
}
1、属性和方法要放在一起
2、访问控制
private:私有权限 除本类外都不可访问
protected:保护权限 除本类和本类的子类外都不可访问
public:公有权限 都可访问
默认访问权限:私有权限
建议写法:对属性访问关闭,对方法访问开放
struct和class
就目前水平来说:只有一个区别,默认权限不同,class默认私有,struct默认公有
3、初始化和销毁
构造函数:用来对成员变量进行初始化的函数
形式:函数名和类名相同,无返回值
缺省函数:如果没写构造函数,编译器会给类生成默认的构造函数
如果写了自定义的带参构造,编译器就不会生成默认构造,就需要自己手动输入
//缺省构造(无参构造)
Student() {}//编译器生成类似函数<!--无参数,无实体-->
//带参构造函数
Student(std::string name,int age)
//拷贝构造函数
Student(Student& s) //为了避免在从实参传入形参过程中进行拷贝,调用拷贝构造造成无限递归,这里使用引用
{
m_name = s.m_name;
m_age = s.m_age;
}
//拷贝构造函数调用
Student s1=s;
Student s1(s);
//赋值运算符的重载
编译器自定义:inline Student &Student::operator=(const Student &)
手动定义: void operator=(Student &s)
{
m_name = s.m_name;
m_age = s.m_age;
}
Student s={"zhangsan",21};
Student s1;
s1 = s;
//析构函数:无返回值,函数名和类名相同 函数名前加一个 ~ 无参数
//自动调用
//析构函数不能重载
~Student(){}
构造函数是可以重载的
raii:
资源获取及初始化
ps:如果以后有很多指针,统一在构造里申请,在析构里释放,能避免很多段错误
浅拷贝问题:当类中有指针成员在构造里申请空间,在析构中释放,如果拷贝构造只是简单的地址拷贝,会导致两个对象中的指针成员指向同一块堆上内存,导致二次释放
class Test
{
public:
Test()
{
a = new int;
}
~Test()
{
delete a;
}
private:
int *a;
};
int main()
{
Test t;
Test t1;
t1 = t;
return 0;
}
深拷贝:指针成员不能进行简单的地址拷贝,进行内存拷贝
class Test
{
public:
Test()
{
std::cout << "构造函数" << std::endl;
a = new int;
}
//深度拷贝
Test(Test& t)
{
std::cout << "拷贝构造函数" << std::endl;
a = new int;
*a = *t.a;
}
void operator=(Test &t)
{
std::cout << "赋值运算符的重载" << std::endl;
delete a;
a = new int;
*a = *t.a;
}
~Test()
{
std::cout << "析构函数" << std::endl;
delete a;
}
private:
int *a;
};
int main()
{
Test t;
Test t1;
t1 = t;
return 0;
}
匿名周期
Test();//匿名对象:声明周期只有一行 是右值(将亡值也是右值)
Test t1 = Test(); //没有拷贝,只构造一个对象
//RVO优化:返回值优化
//返回局部对象,不会发生拷贝,而是直接夺取内存,少一次拷贝构造 ps:老版本编译器没有这个优化能力
//左值是不会优化的
Test func()
{
Test t;
return t;//返回局部变量,将亡值
}
int main()
{
Test t1 = func();
return 0;
}
static:
1、修饰局部变量
2、修饰全局变量
3、修饰成员变量:该变量由整个类共享
4、修饰成员函数:该函数不依赖任何对象进行调用
class Test
{
private:
public:
static int a;//静态成员变量
//不属于某个特定的对象,属于整个类
};
//不能在构造函数中初始化,只能在类外单独定义
int Test::a = 0;
在类中定义的静态变量,相当于静态全局区的变量在类中有一个声明,不属于那个类,所以也不能随着构造函数初始化,只能在类外单独定义,同时,sizeof(t)的时候a的大写也不被包含在内
class Test
{
public:
Test()
{
a++;
}
~Test()
{
a--;
}
public:
static int a;//静态成员变量
//不属于某个特定的对象,属于整个类
};
int Test::a = 0;
int main()
{
Test *t1 = new Test;
Test *t2 = new Test;
delete t1;
delete t2;
std::cout << Test::a << std::endl;
return 0;
}
在构造函数时加一,析构函数时减一,可以检验程序内存是否泄漏
静态成员函数:
class Test
{
public:
static void func()//静态成员函数
//不需要依赖对象调用
//普通成员变量不能使用
//使用静态成员变量
{
a++;
}
static int a;
};
int main()
{
Test::func();
}
Singleton:单例模式
单例模式:一个类只能创建一个对象
1、构造函数私有化
2、提供一个静态函数接口,返回对象指针
3、定义静态局部变量,返回变量指针
//单例模式:一个类只能创建一个对象
class Singleton
{
public:
static Singleton* GetInstance()
{
static Singleton s;//只初始化一次
return &s;
}
void show()
{
std::cout << "helloworld\n";
}
private:
Singleton(){}//构造函数私有化
};
int main()
{
Singleton *s1 = Singleton::GetInstance();
s1->show();
return 0;
}
因为构造函数初始化后无法新建变量,但又需要获取一个变量所以利用静态函数静态变量的特性,可以通过整个类调用,用它来给一个变量。
this指针
class Test
{
public:
Test(int a)
{
std::cout << "构造函数" << std::endl;
m_a = a;
}
void show()
{
//this指针:由编译器自己生成,指向当前对象
std::cout <<this->m_a << std::endl;
}
private:
int m_a;
};
int main()
{
Test t1(1);
Test t2(1);
t1.show();
return 0;
}
在类函数中无法辨别m_a是t1还是t2的,其实编译器会自己生成一个this对象,指向当前对象,使自己能够辨别
ps:静态成员函数没有this指针
const
const变量初始化:初始化列表
class Test
{
//初始化列表 :在构造函数后加: 后更要定义的变量
//const变量必须在初始化列表中初始化
public:
Test():a(0) //也可以 Test(int t):a(0),b(b); 常量也可以在初始化列表中初始化
{
}
void show()
{
std::cout <<this->a << std::endl;
}
private:
const int a;
};
int main()
{
Test t;
t.show();
return 0;
}
const的应用:
class Test
{
public:
//修饰返回值:返回值不可修改(只有用指针或引用接才会生效)其他类型变量都是拿的值
// const int& func()
// {
// return a;
// }
//修饰形参:不可修改,保证传进来的数据的安全(指针和引用)
// void func(const int &a)
// {
// }
//常成员函数:类的成员变量不能修改 ps:但将成员变量用引用传进来则可以修改
void func()const
{
}
private:
int a;
};
int main()
{
Test t;
// int a = t.func();
// const int &b = t.func();
return 0;
}
expilicit:
防止隐式转换
class Integer
{
public:
//防止隐式转换 由explicit定义的函数禁止隐式转换
explicit Integer(int a):a(a)
{
}
void show()
{
std::cout << a << std::endl;
}
private:
int a;
};
int main()
{
Integer i(10);
i = 20;// i=Integer(20) 构造函数的隐式转换:会把符合构造函数形式的变量变换成匿名对象
//所以为了私有值避免值被修改,要用explicit定义
i.show();
return 0;
}
new delete 和malloc free?
1、new delete :关键字 malloc free: 库函数
2、new不需要计算内存大小 malloc要手动计算
3、new不需要强转指针 malloc需要
4、new不需要判断返回值,会抛异常 malloc会判返回值
5、new delete 会调用构造和析构函数 malloc free 不会
友元
友元函数
友元类:1、友元关系是单向的
2、友元关系不能传递,不可继承
class Point
{
public:
Point():x(0),y(0) {}
Point(int x, int y) : x(x), y(y) {}
void show()
{
std::cout << "(" << x <<","<<y<< ")" << std::endl;
}
//友元函数声明 可以调用私有权限的成员,如果一个类中友元声明过多,会显得臃肿
// friend double Distance(const Point &p1, const Point &p2);
//声明友元类
friend class Tool;
private:
int x;
int y;
};
//工具类
class Tool
{
public:
static double Distance(const Point &p1, const Point &p2)//为了避免使用工具类还需要新建工具变量,用static
{
return std::sqrt(std::pow((p1.x-p2.x),2)+std::pow((p1.y-p2.y),2));
}
};
int main()
{
Point p1;
p1.show();
Point p2(1, 2);
p2.show();
std::cout << Tool::Distance(p1, p2) << std::endl;
return 0;
}