一、类的定义
class 类名{
成员属性
构造函数
析构函数
成员函数
}
Person.h
#include <string>
#include <iostream>
using namespace std;
class Person {
int m_age;
string m_name;
Person();
Person(int age,string name);
~Person();
int getAge();
void setAge(int age);
string getName();
void setName(string name);
};
Person.cpp
#include "Person.h"
Person::Person() {}
Person::Person(int age, string name) :m_age(age), m_name(name) {}
Person::~Person() {
cout <<"析构函数调用" << endl;
}
int Person::getAge() {
return m_age;
}
void Person::setAge(int age) {
m_age = age;
}
string Person::getName() {
return m_name;
}
void Person::setName(string name) {
m_name = name;
}
- 如果要使用string类型的话,第一、需要添加#include <string>;第二、需要添加std的命名空间(using namespace std;)或者使用std::string
- 类里面的所有成员属性和函数默认都是私有的(private)
只需要调整访问权限为public即可。
二、类的实例对象创建
2.1、声明的时候,在栈中创建对象
Person person;
- 在栈中创建的对象会随着main函数执行的结束而销毁
2.2、使用new,在堆中创建对象
Person *person = new Person();
- 使用new创建的是一个指针
- main函数执行结束,person对象并没有调用析构函数,说明该对象还没有销毁,需要手动调用delete
2.3、使用malloc,在堆中创建对象
Person *person = (Person*)malloc(sizeof(Person));
- malloc创建的也是一个指针
- malloc创建对象的时候,没有调用构造方法,只是在堆中开辟了所需要大小的内存空间
- 需要调用free方法,来销毁创建的对象,释放内存
- 无论是使用malloc创建对象,以及使用free销毁对象,都不会调用类的构造函数和析构函数
三、类的成员属性的初始化
3.1、无构造函数的类
Person.h
#include <string>
#include <iostream>
using namespace std;
class Person {
int m_age;
string m_name;
public:
~Person();
int getAge();
void setAge(int age);
string getName();
void setName(string name);
};
Person.cpp
#include "Person.h"
Person::~Person() {
cout <<"析构函数调用" << endl;
}
int Person::getAge() {
return m_age;
}
void Person::setAge(int age) {
m_age = age;
}
string Person::getName() {
return m_name;
}
void Person::setName(string name) {
m_name = name;
}
- 对于无构造方法的类,只有只用new创建的对象的成员属性才会被初始化。
3.2、有构造函数的类
Person.h
#include <string>
#include <iostream>
using namespace std;
class Person {
int m_age;
string m_name;
public:
Person();
Person(int age,string name);
~Person();
int getAge();
void setAge(int age);
string getName();
void setName(string name);
};
Person.cpp
#include "Person.h"
Person::Person() {
cout << "构造方法调用" << endl;
}
Person::Person(int age, string name) :m_age(age), m_name(name) {}
Person::~Person() {
cout <<"析构函数调用" << endl;
}
int Person::getAge() {
return m_age;
}
void Person::setAge(int age) {
m_age = age;
}
string Person::getName() {
return m_name;
}
void Person::setName(string name) {
m_name = name;
}
- 如果类有构造函数,但是构造函数里面并没有对成员属性进行初始化的话,无论何种方式创建对象,对象的成员属性都不会被初始化
四、类的构造函数
4.1、定义
构造函数,是一种特殊的方法(没有返回值,方法名和类名相同)。 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值。
4.2、实现
Test.h
#pragma once
class Test
{
public:
int number_;
Test();
Test(int number);
};
Test.cpp
#include "Test.h"
Test::Test() {}
Test::Test(int number) {
number_ = number;
}
- 构造函数没有返回值
- 构造函数的函数名称与类名相同
- 构造函数可以重载
4.3、初始化列表
4.3.1、格式
标准构造函数格式:
Test::Test(int number) {
number_ = number;
}
初始化列表格式:
Test::Test(int number) :number_(number) {}
4.3.2、优势
- 传入参数可以是表达式
int a = 10;
Test* test = new Test(a+10);
- 传入参数可以是函数
#include <iostream>
#include "Test.h"
using namespace std;
int add(int a,int b);
int main() {
int a = 10;
Test* test = new Test(a+add(a,2));
cout << test->number_ << endl;
return 0;
}
int add(int a, int b) {
return a * b;
}
- 搭配默认参数,创建对象时,构造函数更灵活
PS:如果是采用声明和实现分离的方式定义类的话,初始化列表是写在头文件中的
Test.h
#pragma once
class Test
{
public:
int number_;
Test(int number=0) :number_(number) {};
};
#include <iostream>
#include "Test.h"
using namespace std;
int main() {
Test *test=new Test();
cout << test->number_ << endl;
Test *test1=new Test(18);
cout << test1->number_ << endl;
return 0;
}
4.4、构造函数互调
- 构造函数的互调也是使用初始化列表的形式实现的
#include "Test.h"
Test::Test() :Test(10) {}
Test::Test(int number) {
number_ = number;
}
4.5、拷贝构造函数
拷贝构造函数是构造函数的一种,也称复制构造函数,它只有一个参数,参数类型是本类的引用。
拷贝构造函数的参数可以是 const 引用,也可以是非 const 引用。 一般使用前者,这样既能以常量对象(初始化后值不能改变的对象)作为参数,也能以非常量对象作为参数去初始化其他对象。
如果类不写拷贝构造函数,编译器就会自动生成拷贝构造函数。大多数情况下,其作用是实现从源对象到目标对象逐个字节的拷贝,即使得目标对象的每个成员变量都变得和源对象相等。编译器自动生成的拷贝构造函数称为“默认拷贝构造函数”。
PS:默认构造函数(即无参构造函数)不一定存在,但是拷贝构造函数总是会存在。
4.5.1、对象赋值的时候,会调用拷贝构造函数
Copy.h
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Copy
{
public:
int mAge;
string mName;
public:
Copy(int age=0, string name="") :mAge(age), mName(name) {}
Copy(const Copy& copy) :mAge(copy.mAge), mName(copy.mName) {};
~Copy();
};
Copy.cpp
#include "Copy.h"
Copy::Copy(const Copy& copy) {
cout << "拷贝构造函数" << endl;
}
Copy::~Copy()
{
}
main.cpp
#include <iostream>
#include "Copy.h"
using namespace std;
int main() {
Copy copy1;
Copy copy2 = copy1;
getchar();
return 0;
}
4.5.2、对象作为函数的入参时会调用对象的拷贝构造函数
Utils.h
#pragma once
#include<string>
#include "Copy.h"
using namespace std;
class Utils
{
public:
const static string BASIC_URL;
static string formatInt2String(int number);
static void display(Copy copy);
};
Utils.cpp
#include "Utils.h"
const string Utils::BASIC_URL = "http://www.baidu.com";
string Utils::formatInt2String(int number)
{
return to_string(number);
}
void Utils::display(Copy copy) {
cout << copy.mName << endl;
}
main.cpp
#include <iostream>
#include "Copy.h"
#include "Utils.h"
using namespace std;
int main() {
Copy copy1;
Utils::display(copy1);
getchar();
return 0;
}
PS:为了避免函数调用产生多余的临时拷贝,所以,当对象作为形参时,要使用引用或者指针。
此时函数调用就没有调用对象的拷贝构造函数。
4.6、运算符重载
运算符重载是一种形式的多态,运算符重载可使代码看起来更自然。
operator + <操作符>+()
4.6.1、可重载的运算符
+ | - | * | / | % | ^ |
---|---|---|---|---|---|
& | | | ~= | ! | = | < |
> | += | -= | /= | *= | %= |
^= | &= | |= | << | >> | <<= |
>>= | == | != | <= | >= | && |
|| | ++ | - - | , | ->* | -> |
() | [] | new | delete | new [] | delete [] |
OperatorOverride.h
#pragma once
class OperatorOverride
{
public:
OperatorOverride(int age=16) :mAge(age) {}
~OperatorOverride();
int getAge();
void setAge(int age);
private:
int mAge;
};
OperatorOverride.cpp
#include "OperatorOverride.h"
OperatorOverride::~OperatorOverride()
{
}
int OperatorOverride::getAge()
{
return mAge;
}
void OperatorOverride::setAge(int age)
{
mAge = age;
}
4.6.2、一元运算符重载
一元运算符即只对一个操作数进行操作的运算符,例如:!obj、-obj、++obj 、obj++ 或 obj-- 等等
OperatorOverride.h
#pragma once
#include <iostream>
using namespace std;
class OperatorOverride
{
public:
OperatorOverride(int age=16,bool adult=0) :mAge(age),isAdult(adult) {}
~OperatorOverride();
int getAge();
void setAge(int age);
bool getIsAdult();
void setIsAdult(bool adult);
void display();
void operator !();
OperatorOverride& operator ++();
OperatorOverride operator ++(int);
private:
int mAge;
bool isAdult;
};
OperatorOverride.cpp
#include "OperatorOverride.h"
OperatorOverride::~OperatorOverride()
{
}
int OperatorOverride::getAge()
{
return mAge;
}
void OperatorOverride::setAge(int age)
{
mAge = age;
}
bool OperatorOverride::getIsAdult()
{
return isAdult;
}
void OperatorOverride::setIsAdult(bool adult)
{
isAdult = adult;
}
void OperatorOverride::display()
{
cout << "Age is:" << mAge << endl;
cout << "Are you is adult:" << isAdult << endl;
}
void OperatorOverride::operator!()
{
isAdult = !isAdult;
}
OperatorOverride& OperatorOverride::operator++()
{
mAge += 1;
return *this;
}
OperatorOverride OperatorOverride::operator++(int)
{
OperatorOverride o(mAge,isAdult);
mAge++;
return o;
}
main.cpp
#include <iostream>
#include "OperatorOverride.h"
using namespace std;
int main() {
OperatorOverride oo1(18,false);
oo1.display();
cout << "-----------初始化------------" << endl;
!oo1;
oo1.display();
cout << "-----------重载!------------" << endl;
++oo1;
oo1.display();
cout << "-----------重载前置++------------" << endl;
(oo1++).display();
cout << "-----------重载后置++------------" << endl;
getchar();
return 0;
}
4.6.2、二元运算符重载
二元运算符即需要两个参数的运算符,例如:加运算符(+)、减运算符(-)、乘运算符(*)、除运算符(/)。
OperatorOverride.h
OperatorOverride operator +(OperatorOverride& oo);
OperatorOverride.cpp
OperatorOverride OperatorOverride::operator+(OperatorOverride& oo)
{
return OperatorOverride(mAge+oo.getAge());
}
4.6.3、关系运算符重载
C++ 允许重载任何一个关系运算符(例如 < 、 > 、 <= 、 >= 、 == 等),重载后的关系运算符可用于比较类的对象。
OperatorOverride.h
bool operator >(OperatorOverride& oo) const;
bool operator <(OperatorOverride& oo)const;
bool operator ==(OperatorOverride& oo)const;
OperatorOverride.cpp
bool OperatorOverride::operator>(OperatorOverride& oo) const
{
return mAge>oo.getAge();
}
bool OperatorOverride::operator<(OperatorOverride& oo)const
{
return mAge < oo.getAge();
}
bool OperatorOverride::operator==(OperatorOverride& oo)const
{
return mAge==oo.getAge();
}
main.cpp
#include <iostream>
#include "OperatorOverride.h"
using namespace std;
int main() {
OperatorOverride oo1(18,true);
OperatorOverride oo2(20, false);
bool state = oo1 > oo2;
cout << "oo1>oo2:" << state<< endl;
state = oo1 < oo2;
cout << "oo1<oo2:" << state << endl;
state = oo1 == oo2;
cout << "oo1==oo2:" << state << endl;
getchar();
return 0;
}
4.7、补充
4.7.1、如果不创建构造函数,创建对象的时候会创建默认的构造函数吗?
Test.h
#pragma once
class Test
{
public:
int number_=0;
};
- 如果成员属性默认赋值,则会生成默认的构造函数,还会调用默认的构造函数
#pragma once
class Test
{
public:
int number_;
};
- 如果成员属性没有赋值,则不会生成默认的构造函数。
4.7.2、创建对象的时候,new test和new Test()有什么区别
- 如果类没有显示声明构造函数,都不会调用创建构造函数,但是带括号的会初始化成员属性
- 如果自定义构造函数,则都会调用构造方法,并且都不会初始化成员属性
五、静态(static)成员属性和函数
5.1.1、静态成员属性
静态成员属性实际上是类域中的全局变量,且类的静态成员属性在类内只能声明**,定义和初始化必须在类外**
静态成员属性被类的所有对象共享,包括该类的派生类对象,所以静态成员属性为类内的全局变量
Utils.h
#pragma once
#include<string>
#include "Copy.h"
using namespace std;
class Utils
{
public:
const static string BASIC_URL;
};
Utils.cpp
#include "Utils.h"
const string Utils::BASIC_URL = "http://www.baidu.com";
5.1.2、静态成员函数
类的静态成员函数可以在没有定义任何对象前使用,即无须创建任何对象实例就可以使用此成员函数
静态成员函数不可调用类的非静态成员,且静态成员函数不包含this指针,非静态成员必须与特定对象相对
Utils.h
#pragma once
#include<string>
#include "Copy.h"
using namespace std;
class Utils
{
public:
static string formatInt2String(int number);
static void display(Copy ©);
};
Utils.cpp
#include "Utils.h"
string Utils::formatInt2String(int number)
{
return to_string(number);
}
void Utils::display(Copy ©) {
cout << copy.mName << endl;
}