定义类时的注意点
1. 类内的任何成员变量和成员函数最好是都要明确设置其访问控制属性。
访问控制属性包括:公有类型(public),私有类型(private)和保护类型(protected)
虽然,不设置访问控制属性的默认是私有类型。比如
#include <iostream>
class Test {
int pr;
public:
Test(int i);
~Test() {};
int test_;
};
Test::Test(int i) {
test_ = i;
}
int main() {
Test test(1);
std::cout << "hello = " << test.test_<< std::endl;
// std::cout << "hello = " << test.pr<< std::endl; // Error
}
变量 pr 是私有类型,外部访问会报错。
还有
#include <iostream>
class Test {
Test(int i);
~Test() {};
public:
int test_;
};
Test::Test(int i) {
test_ = i;
}
int main() {
Test test(1);
std::cout << "hello = " << test.test_<< std::endl;
}
构造函数在对象被创建时,就会被自动调用。
构造函数不设置访问控制属性,默认 private,那么外部调用构造函数,进行类初始化就会报错。
所以,
类内任何成员变量和成员函数最好全部设置明确的访问控制属性。
明明白白,干干净净。
2. 对象
声明对象的方式
类名 对象名;
对象通过 “.” 操作符,访问类的成员变量和成员函数
对象名.成员变量
对象名.成员函数(参数表)
这里要区别指针通过,“->” 操作符,访问类的成员变量和成员函数
#include <iostream>
#include <memory>
class Test {
Test(int i);
~Test() {};
public:
int test_;
};
Test::Test(int i) {
test_ = i;
}
int main() {
std::shared_ptr<Test> ptr1 = std::make_shared<Test>(1);
std::cout << "hello = " << ptr1->test_<< std::endl;
}
在类的外部,只能访问到访问控制属性为 public 的类的成员变量和成员函数
在类的内部,可以访问类的全部成员变量和成员函数
3. 成员函数
类的成员函数实现方式
返回值类型 类名::成员函数名(参数表)
{
函数体;
}
类的成员函数需要用类名限制。
这里需要注意返回值类型,处理不好,会报内存泄露
比如
#include <iostream>
#include <memory>
namaspace test::pc{
struct Element{
Element(int w, int h):w_(w), h_(h){}
int w_;
int h_;
};
using ElementPtr = std::shared_ptr<Element>;
class Test {
Test(int i);
~Test() {};
test::pc::ElementPtr handle(){};
public:
int test_;
};
Test::Test(int i) {
test_ = i;
}
test::pc::ElementPtr Test::handle(){
return test::pc::ElementPtr
}
int main() {
std::shared_ptr<Test> ptr1 = std::make_shared<Test>(1);
std::cout << "hello = " << ptr1->handle()<< std::endl;
}
}
这个例子不能运行,但是能大概表现想要表达的东西。
首先,ElementPtr 是一个指向 Element 的结构体,在Test 类中,有一个 handle() 成员函数,
handle() 成员函数的返回值类型,是一个指向 Element 的结构体共享指针。
如果在实现 handle() 成员函数的代码中,没有一个return,那么就会报
corrupted double-linked list
函数返回值是什么,就 return 什么类型的数据。
void 是没有返回值。
所以,不要小看平时一些不起眼的细节,这些细节没注意,就会犯错。
4. 构造函数
构造函数的声明与实现
class 类名{
public:
类名(形参表);
};
类名::类名(形参表){
函数体;
}
构造函数,是类的成员函数
构造函数名与类名相同
构造函数通常被声明为公有函数
构造函数可以直接访问类的所有成员变量和成员函数
构造函数可以重载,可以是无参构造函数,也可以是带默认形参值的构造函数,还可以是内联函数
5. 复制构造函数
复制构造函数的声明与实现
class 类名{
public:
类名(形参表);
类名(类名& 对象名);
};
// 构造函数
类名::类名(形参表){
函数体;
}
// 复制构造函数
类名::类名(类名& 对象名){
函数体;
}
(1)复制构造函数被调用的三种情况
(1) 用一个对象初始化另一个对象
(2) 函数的形参是一个对象
(3) 函数的返回值是一个对象
(2)Note:对象间赋值并不导致复制构造函数被调用
#include <iostream>
class CMyclass
{
public:
int n;
CMyclass() {};
CMyclass( CMyclass & c) { n = 2 * c.n ; }
};
int main()
{
CMyclass c1,c2;
c1.n = 5;
c2 = c1;
CMyclass c3(c1);
std::cout <<"c2.n=" << c2.n << std::endl;
std::cout <<"c3.n=" << c3.n << std::endl;
return 0;
}
输出:
c2.n=5
c3.n=10
(3)实参未必是形参的拷贝。
因为实参如果是对象,形参初始化时调用复制构造函数时,复制构造函数并没有实现完整的复制,那么,形参未必是实参的拷贝。
(4)复制构造函数的作用
大多数情况下,复制构造函数的作用是
实现从源对象到目标对象逐个字节的复制
即使得目标对象的每个成员变量都变得和源对象相等。
(5)常量引用参数的使用
void fun(CMyclass obj_ )
{
std::cout << "fun" << std::endl;
}
这样的函数,调用时生成形参会引发复制构造函数调用,开销比较大。
所以可以考虑使用 CMyclass & 引用类型作为参数。
如果希望确保实参的值在函数中不应被改变,那么可以加上const 关键字:
void fun(const CMyclass & obj)
{
//函数中任何试图改变 obj值的语句都将是变成非法
}
常量对象:初始化后,值不能改变的对象