C++ 类和对象

一 基本概念
类、对象、成员变量、成员函数。
面向对象三大概念:封装、继承、多态。

二 类的封装
1 封装,是面向对象程序设计最基本的特性。把数据(属性)和函数(操作)合成一个整体,
2 类成员的访问控制
1)public修饰的成员变量 方法 在类的内部 类的外部都能使用
2)protected:修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ;其他类的外部不能被使用
3) private: 修饰的成员变量方法 只能在类的内部使用 不能在类的外部

三 构造函数
1 C++中的类可以定义与类名相同的特殊成员函数。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。
2 构造函数分类,建立对象时调用。
1)无参数构造函数: Test t1, t2;
2)带参数的构造函数
a)括号法 :Test5 t1(10);
b) 等号法:Test5 t2 = (20, 10)
c 直接调用:Test5 t3 = Test5(30);
3)拷贝构造函数
a) 用一个对象去初始化另一个类:Test4 t2 = t1; //用t1来初始化 t2
b) 括号法:同上,写法不同 Test4 t2(t1); //用t1对象 初始化t2对象
c)函数调用,用匿名对象初始化其它对象

#include <iostream>
using namespace std;
class Location 
{ 
public:
Location( int xx = 0 , int yy = 0 ) 
{ 
    X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ; 
}
//copy构造函数  完成对象的初始化
Location(const Location & obj) //copy构造函数 
{
    X = obj.X; Y = obj.Y;
}
~Location() 
{ 
    cout << X << "," << Y << " Object destroyed." << endl ; 
}
int  GetX () { return X ; }     int GetY () { return Y ; }
private :   int  X , Y ;
} ;

//业务函数  形参是一个元素
void f(Location p)
{
cout<<p.GetX()<<endl;
}

void playobj()
{
Location  a(1, 2);
Location  b = a;
cout<<"b对象已经初始化完毕"<<endl;

 f(b); //b实参取初始化形参p,会调用copy构造函数
}

void main()
{
playobj();
cout<<"hello..."<<endl;
system("pause");
return ;
}

d) 用匿名对像赋值其它对象

#include <iostream>
using namespace std;

class Location 
{ 
public:
Location( int xx = 0 , int yy = 0 ) 
{ 
    X = xx ;  Y = yy ;  cout << "Constructor Object.\n" ; 
}

//copy构造函数  完成对象的初始化
Location(const Location & obj) //copy构造函数 
{
    X = obj.X; Y = obj.Y;
}
~Location() 
{ 
    cout << X << "," << Y << " Object destroyed." << endl ; 
}
int  GetX () { return X ; }     int GetY () { return Y ; }
private :   int  X , Y ;
} ;
   Location g()
{
Location A(1, 2);
return A;
}
void objplay3()
{
//用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
Location m = g(); 
printf("匿名对象,被扶正,不会析构掉\n");
cout<<m.GetX()<<endl;;
}
void objplay4()
{
 //用匿名对象 赋值给 m2后, 匿名对象被析构
Location m2(1, 2);
m2 = g();
printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
cout<<m2.GetX()<<endl;;
}
void main()
{
objplay3();
objplay4();
cout<<"hello..."<<endl;
system("pause");
return ;
}

3 构造函数的调用规则
1) 当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2) 当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3)当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数。
4)默认拷贝构造函数成员变量简单赋值

四 析构函数
1)C++中的类可以定义一个特殊的成员函数清理对象
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用

五 深拷贝与浅拷贝

#include <iostream>
using namespace std;

class  Name
{
public:
Name(const char *myp)
{
    m_len = strlen(myp);
    m_p =(char *) malloc(m_len + 1); //
    strcpy(m_p, myp);
}

   //解决方案: 手工的编写拷贝构造函数 使用深copy
Name(const Name& obj1)
{
    m_len = obj1.m_len;
    m_p = (char *)malloc(m_len + 1);
    strcpy(m_p, obj1.m_p);
}

~Name()
{
    if (m_p != NULL)
    {
        free(m_p);
        m_p = NULL;
        m_len = 0;
    }
}
protected:
private:
char *m_p ;
int m_len; 
};

//对象析构的时候 出现coredump
void objplaymain()
{
Name obj1("abcdefg");
Name obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
Name obj3;

obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
}

void main91()
{
objplaymain();
cout<<"hello..."<<endl;
system("pause");
return ;
}

六 对象初始化列表
1 出现原因
1)在B类中集合了一个A类对象(A类设计了构造函数),根据构造函数的调用规则,设计了构造函数,就要调用。没有机会初始化A,新的语法派上用场。即对象初始化列表。
2)当类成员中含有一个const成员变量时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化。
3) 拷贝构造函数后面也可以跟初始化列表。

2 语法规则

Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
{
// some other assignment operation
}

3 执行顺序
1) 成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
2) 初始化列表先于构造函数的函数体执行

4构造函数和析构函数的调用顺序研究
1)当类中有成员变量是其它类的对象时,首先调用成员变量的构造函数,调用顺序与声明顺序相同;之后调用自身类的构造函数
2)析构函数的调用顺序与对应的构造函数调用顺序相反
3)初始化列表用来给const属性赋值。

#include <iostream>
using namespace std;

class A
{
public:
A(int _a)
{
    a = _a;
    cout << "构造函数" << "a" << a << endl;
}

~A()
{
    cout << "析构函数" << "a" << a << endl;
}

protected:
private:
int a;
};

//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
B(int _b1, int _b2) : a1(1), a2(2), c(0)
{

}

B(int _b1, int _b2, int m, int n) : a1(m), a2(n), c(0)
{
    b1 = _b1;
    b2 = _b2;
    cout <<"B的构造函数"<<endl;
}
~B()
{
    cout<<"B的析构函数" <<endl;
}

protected:
private:
int b1;
int b2;
A a2;
A a1;
const int c;
};
//2 先执行 被组合对象的构造函数 
//如果组合对象有多个,按照声明顺序, 而不是按照初始化列表的顺序
//析构函数 : 和构造函数的调用顺序相反
//3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系.
//4 初始化列表 用来 给const 属性赋值 
void obj10play()
{
//A a1(10);
//B ojbB(1, 2);
//1参数传递 
B ojbB2(1, 2,3, 4);
//2 调用顺序
return ;
}
void main100()
{
obj10play();
system("pause");
}

七 综合练习
//对象做函数参数
//1 研究拷贝构造
//2 研究构造函数,析构函数的调用顺序

//总结 构造和析构的调用顺序

#include "iostream"
using namespace std;

class ABCD 
{
public:
ABCD(int a, int b, int c)
{
    this->a = a;
    this->b = b;
    this->c = c;
    printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
}
~ABCD()
{
        printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
}
int getA() 
{
    return this->a;
}
protected:
private:
int a;
int b;
int c;
};


class MyE
{
public:
MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
{
    cout<<"MyD()"<<endl;
}
~MyE()
{
    cout<<"~MyD()"<<endl;
}
MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
{
    printf("MyD(const MyD & obj)\n");
}

protected:

public:
ABCD abcd1; //c++编译器不知道如何构造abc1
ABCD abcd2;
const int m;
};

int doThing(MyE mye1)
{
printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA()); 
return 0;
}

int run2()
{
MyE myE;
doThing(myE);
return 0;
}

int run3()
{
printf("run3 start..\n");

//ABCD(400, 500, 600); //临时对象的生命周期  

ABCD abcd = ABCD(100, 200, 300);
//若直接调用构造函数呢?
//想调用构造函数对abc对象进行再复制,可以吗?
//在构造函数里面调用另外一个构造函数,会有什么结果?

printf("run3 end\n");
return 0;
}

int main()
{
run2();
run3();
system("pause");
return 0;
}

在构造中调用构造是一种危险行为。

#include "iostream"
using namespace std;
//构造中调用构造是危险的行为
class MyTest
{
public:
MyTest(int a, int b, int c)
{
    this->a = a;
    this->b = b;
    this->c = c;
}

MyTest(int a, int b)
{
    this->a = a;
    this->b = b;

    MyTest(a, b, 100); //产生新的匿名对象
}
~MyTest()
{
    printf("MyTest~:%d, %d, %d\n", a, b, c);
}

protected:
private:
int a;
int b;
int c;

public:
int getC() const { return c; }
void setC(int val) { c = val; }
};

int main()
{
MyTest t1(1, 2);
printf("c:%d", t1.getC()); //此时输出的是一个乱码。因为直接调用一个构造函数会产生一个匿名对象,其生命周期只有被调用的那一行,然后被析构。生成的匿名对象和我们定义的那个t1没有什么关系。 
system("pause");
return 0;
}

八 对象的动态建立和释放
C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。
注意: new和delete是运算符,不是函数,因此执行效率高。

class Test
{
public:
Test(int _a)
{
    a = _a;
    cout<<"构造函数执行" <<endl;
}

~Test()
{
    cout<<"析构函数执行" <<endl;
}
private:
int a;
};

//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数   delete操作符 能执行类的析构函数
void main()
{
//c 
Test *pT1 = (Test *)malloc(sizeof(Test));
free(pT1);

//c++
Test *pT2 = new Test(10);
delete pT2;

cout<<"hello..."<<endl;
system("pause");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值